diff options
author | Zahary Karadjov <zahary@gmail.com> | 2016-07-29 04:56:14 +0300 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2017-03-24 16:58:15 +0200 |
commit | 66e0f0e19cc47caa01aa898988eb7452d07e22cf (patch) | |
tree | fe1fadc292d04edc1e8240e1a0cbfc04d050343c | |
parent | 0d8a503e450b1d240fb5ab914f40aa30d7b6cd8b (diff) | |
download | Nim-66e0f0e19cc47caa01aa898988eb7452d07e22cf.tar.gz |
concept type params inference working in basic examples
-rw-r--r-- | compiler/ast.nim | 1 | ||||
-rw-r--r-- | compiler/semexprs.nim | 4 | ||||
-rw-r--r-- | compiler/semtypes.nim | 3 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 38 | ||||
-rw-r--r-- | tests/concepts/tstackconcept.nim | 49 |
5 files changed, 93 insertions, 2 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 26305cf3b..4a25e0336 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -482,6 +482,7 @@ type tfHasStatic tfGenericTypeParam tfImplicitTypeParam + tfInferrableTypeClassTypeParam tfWildcard # consider a proc like foo[T, I](x: Type[T, I]) # T and I here can bind to both typedesc and static types # before this is determined, we'll consider them to be a diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4d698dbfc..cb16bf406 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -318,7 +318,9 @@ proc isOpImpl(c: PContext, n: PNode): PNode = else: result = newIntNode(nkIntLit, 0) else: - var t2 = n[2].typ.skipTypes({tyTypeDesc}) + var rhsOrigType = n[2].typ + discard inferTypeClassParam(c, t1, rhsOrigType) + var t2 = rhsOrigType.skipTypes({tyTypeDesc}) maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index e86b527d6..e3c3123f9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -883,7 +883,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, for i in 1 .. <paramType.len: let lifted = liftingWalk(paramType.sons[i]) if lifted != nil: paramType.sons[i] = lifted - when false: + + if paramType.base.lastSon.kind == tyUserTypeClass: let expanded = instGenericContainer(c, info, paramType, allowMetaTypes = true) result = liftingWalk(expanded, true) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 164fd0999..b279e7d2d 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -585,6 +585,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, dec c.inTypeClass closeScope(c) + var typeParams: seq[(PSym, PType)] + if ff.kind == tyUserTypeClassInst: for i in 1 .. <(ff.len - 1): var @@ -607,7 +609,12 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, param = paramSym skType param.typ = makeTypeDesc(c, typ) + if typ.isMetaType: + param.typ.flags.incl tfInferrableTypeClassTypeParam + addDecl(c, param) + typeParams.safeAdd((param, typ)) + #echo "A ", param.name.s, " ", typeToString(param.typ), " ", param.kind for param in body.n[0]: @@ -630,6 +637,13 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, var checkedBody = c.semTryExpr(c, body.n[3].copyTree) if checkedBody == nil: return isNone + + # The inferrable type params have been identified during the semTryExpr above. + # We need to put them in the current sigmatch's binding table in order for them + # to be resolvable while matching the rest of the parameters + for p in typeParams: + put(m.bindings, p[0].typ, p[1]) + return isGeneric proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool = @@ -1355,6 +1369,27 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) = of isEqual: inc(m.exactMatches) of isNone: discard +proc skipToInferrableParam(tt: PType): PType = + var t = tt + while t != nil: + if tfInferrableTypeClassTypeParam in t.flags: + return t + if t.sonsLen > 0 and t.kind == tyTypeDesc: + t = t.base + else: + return nil + + return nil + +proc inferTypeClassParam*(c: PContext, f, a: PType): bool = + if c.inTypeClass == 0: return false + + var inferrableType = a.skipToInferrableParam + if inferrableType == nil: return false + + inferrableType.assignType f + return true + proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, argSemantized, argOrig: PNode): PNode = var @@ -1363,6 +1398,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, argType = argType c = m.c + if inferTypeClassParam(c, f, argType): + return argSemantized + if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim new file mode 100644 index 000000000..3993ca534 --- /dev/null +++ b/tests/concepts/tstackconcept.nim @@ -0,0 +1,49 @@ +discard """ +output: "20\n10" +msg: ''' +INFERRED int +''' +""" + +import typetraits + +template reject(e: expr) = + static: assert(not compiles(e)) + +type + ArrayStack = object + data: seq[int] + +proc push(s: var ArrayStack, item: int) = + s.data.add item + +proc pop(s: var ArrayStack): int = + return s.data.pop() + +type + Stack[T] = concept var s + s.push(T) + s.pop() is T + +proc genericAlgorithm[T](s: var Stack[T], y: T) = + static: echo "INFERRED ", T.name + + s.push(y) + echo s.pop + +proc implicitGeneric(s: var Stack): auto = + # static: echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name + + return s.pop() + +var s = ArrayStack(data: @[]) + +s.push 10 +s.genericAlgorithm 20 +echo s.implicitGeneric + +reject s.genericAlgorithm "x" +reject s.genericAlgorithm 1.0 +reject "str".implicitGeneric +reject implicitGeneric(10) + |