diff options
-rwxr-xr-x | compiler/ast.nim | 2 | ||||
-rwxr-xr-x | compiler/semexprs.nim | 16 | ||||
-rwxr-xr-x | compiler/seminst.nim | 11 | ||||
-rwxr-xr-x | compiler/semtypes.nim | 19 | ||||
-rwxr-xr-x | compiler/sigmatch.nim | 7 | ||||
-rwxr-xr-x | compiler/types.nim | 4 | ||||
-rwxr-xr-x | doc/manual.txt | 27 | ||||
-rwxr-xr-x | lib/system.nim | 25 | ||||
-rw-r--r-- | tests/run/trettypeinference.nim | 29 |
9 files changed, 104 insertions, 36 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 814784029..c6e4d8318 100755 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 7c147e778..4608d38ef 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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 diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 61210c0f8..8e164531a 100755 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -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() diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5362d6d4a..eeb48a647 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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] diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 7e482b3d2..82a8c9399 100755 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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) diff --git a/compiler/types.nim b/compiler/types.nim index d8879f1b4..b650b49b8 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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: diff --git a/doc/manual.txt b/doc/manual.txt index 4b7dc1aa5..4d9375f30 100755 --- a/doc/manual.txt +++ b/doc/manual.txt @@ -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 diff --git a/lib/system.nim b/lib/system.nim index 575c6abc7..4cfa6ba84 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -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. diff --git a/tests/run/trettypeinference.nim b/tests/run/trettypeinference.nim new file mode 100644 index 000000000..eea5b597d --- /dev/null +++ b/tests/run/trettypeinference.nim @@ -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") + |