diff options
-rwxr-xr-x | compiler/seminst.nim | 44 | ||||
-rwxr-xr-x | compiler/semstmts.nim | 4 | ||||
-rwxr-xr-x | compiler/semtypes.nim | 24 | ||||
-rwxr-xr-x | compiler/sigmatch.nim | 30 | ||||
-rw-r--r-- | tests/run/tunittests.nim | 2 | ||||
-rw-r--r-- | tests/run/uexpr.nim | 13 |
6 files changed, 88 insertions, 29 deletions
diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 1da6a671f..9ec78488b 100755 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -8,6 +8,7 @@ # # This module implements the instantiation of generic procs. +# included from sem.nim proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable, entry: var TInstantiatedSymbol) = @@ -106,6 +107,35 @@ proc sideEffectsCheck(c: PContext, s: PSym) = s.ast.sons[genericParamsPos].kind == nkEmpty: c.threadEntries.add(s) +template nimdbg: expr = c.filename.endsWith"nimdbg.nim" + +proc applyConcreteTypesToSig(genericProc: PSym, concTypes: seq[PType]): PType = + # XXX: This is intended to replace the use of semParamList in generateInstance. + # The results of semParamList's analysis are already encoded in the original + # proc type and any concrete types may be aplied directly over it. + # Besides being more efficient, it will remove the awkward case of + # genericParams == nil in semParamList. + # Currenly, it fails in some cases such as: + # proc inc2*[T](x: var ordinal[T], y = 1) {.magic: "Inc", noSideEffect.} + let sig = genericProc.typ + result = copyType(sig, getCurrOwner(), false) + result.n = sig.n.shallowCopy + + for i in countup(0, sig.len - 1): + let tOrig = sig.sons[i] + if tOrig == nil: continue + let oGenParams = genericProc.ast.sons[genericParamsPos] + if skipTypes(tOrig, skipPtrs).kind in {tyGenericParam}: + var tConcrete = concTypes[tOrig.sym.position] + if i > 0: + let param = sig.n.sons[i].sym.copySym + param.typ = tConcrete + result.n.sons[i] = newSymNode(param) + result.sons[i] = tConcrete + else: + result.sons[i] = tOrig + if i > 0: result.n.sons[i] = sig.n.sons[i] + proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, info: TLineInfo): PSym = # generates an instantiated proc @@ -133,12 +163,14 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry) n.sons[genericParamsPos] = ast.emptyNode # semantic checking for the parameters: - if n.sons[paramsPos].kind != nkEmpty: - removeDefaultParamValues(n.sons[ParamsPos]) - semParamList(c, n.sons[ParamsPos], nil, result) - # XXX: obsoleted - happens in semParamList # - # addParams(c, result.typ.n) - else: + if n.sons[paramsPos].kind != nkEmpty: + if false and nimdbg: + result.typ = applyConcreteTypesToSig(fn, entry.concreteTypes) + addParams(c, result.typ.n, fn.kind) + else: + removeDefaultParamValues(n.sons[ParamsPos]) + semParamList(c, n.sons[ParamsPos], nil, result) + else: result.typ = newTypeS(tyProc, c) addSon(result.typ, nil) result.typ.callConv = fn.typ.callConv diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e5f10ece1..2de496b9a 100755 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -641,8 +641,6 @@ proc semLambda(c: PContext, n: PNode): PNode = illFormedAst(n) # process parameters: if n.sons[paramsPos].kind != nkEmpty: semParamList(c, n.sons[ParamsPos], nil, s) - # XXX: obsoleted - happens in semParamList - # addParams(c, s.typ.n) ParamsTypeCheck(c, s.typ) else: s.typ = newTypeS(tyProc, c) @@ -690,7 +688,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # we have a list of implicit type parameters: n.sons[genericParamsPos] = gp # check for semantics again: - semParamList(c, n.sons[ParamsPos], nil, s) + # semParamList(c, n.sons[ParamsPos], nil, s) else: s.typ = newTypeS(tyProc, c) addSon(s.typ, nil) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index fc45de08c..c0ed5b237 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -520,6 +520,9 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = else: addDecl(c, param) +proc isTypeClass(c: PContext, t: PType): bool = + return t.kind in {tyExpr} + proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType, kind: TSymKind): PType = var @@ -536,7 +539,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, addSon(result.n, res) var check = initIntSet() var counter = 0 - for i in countup(1, sonsLen(n)-1): + for i in countup(1, n.len - 1): var a = n.sons[i] if a.kind != nkIdentDefs: IllFormedAst(a) checkMinSonsLen(a, 3) @@ -569,6 +572,21 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, for j in countup(0, length-3): var arg = newSymS(skParam, a.sons[j], c) arg.typ = typ + if kind notin {skTemplate, skMacro} and isTypeClass(c, typ): + let typeClassParamId = getIdent(":tcls_" & $i & "_" & $j) + if genericParams == nil: + # genericParams is nil when the proc is being instantiated + # the resolved type will be in scope then + var s = SymtabGet(c.tab, typeClassParamId) + arg.typ = s.typ + else: + var s = newSym(skType, typeClassParamId, getCurrOwner()) + s.typ = newTypeS(tyGenericParam, c) + s.typ.sym = s + s.position = genericParams.len + genericParams.addSon(newSymNode(s)) + arg.typ = s.typ + arg.position = counter inc(counter) if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) @@ -831,10 +849,14 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = s = newSymS(skType, a.sons[j], c) s.typ = newTypeS(tyGenericParam, c) of tyExpr: + echo "GENERIC EXPR ", a.info.toFileLineCol # not a type param, but an expression + # proc foo[x: expr](bar: int) what is this? s = newSymS(skGenericParam, a.sons[j], c) s.typ = typ else: + # This handles cases like proc foo[t: tuple] + # XXX: we want to turn that into a type class s = newSymS(skType, a.sons[j], c) s.typ = typ if def.kind != nkEmpty: s.ast = def diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ca65c670f..3db88569f 100755 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -463,14 +463,8 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation = result = isGeneric else: result = typeRel(mapping, x, a) # check if it fits - of tyExpr, tyStmt, tyTypeDesc: - if a.kind == f.kind: - result = isEqual - else: - case a.kind - of tyExpr, tyStmt, tyTypeDesc: result = isGeneric - of tyNil: result = isSubtype - else: nil + of tyExpr, tyStmt, tyTypeDesc: + result = isGeneric else: internalError("typeRel(" & $f.kind & ')') proc cmpTypes*(f, a: PType): TTypeRelation = @@ -514,9 +508,6 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, arg, argOrig: PNode): PNode = - if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate} and - f.kind in {tyExpr, tyStmt, tyTypeDesc}: - return argOrig var r = typeRel(m.bindings, f, a) case r of isConvertible: @@ -528,14 +519,17 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, of isSubtype: inc(m.subtypeMatches) result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c) - of isGeneric: + of isGeneric: inc(m.genericMatches) - result = copyTree(arg) - result.typ = getInstantiatedType(c, arg, m, f) - # BUG: f may not be the right key! - if skipTypes(result.typ, abstractVar).kind in {tyTuple}: - result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) - # BUGFIX: use ``result.typ`` and not `f` here + if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}: + result = argOrig + else: + result = copyTree(arg) + result.typ = getInstantiatedType(c, arg, m, f) + # BUG: f may not be the right key! + if skipTypes(result.typ, abstractVar).kind in {tyTuple}: + result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) + # BUGFIX: use ``result.typ`` and not `f` here of isEqual: inc(m.exactMatches) result = copyTree(arg) diff --git a/tests/run/tunittests.nim b/tests/run/tunittests.nim index b2ec10cdc..e64008e0c 100644 --- a/tests/run/tunittests.nim +++ b/tests/run/tunittests.nim @@ -1,2 +1,2 @@ -import uclosures, utemplates +import uclosures, utemplates, uexpr diff --git a/tests/run/uexpr.nim b/tests/run/uexpr.nim new file mode 100644 index 000000000..06bab375e --- /dev/null +++ b/tests/run/uexpr.nim @@ -0,0 +1,13 @@ +import unittest + +proc concat(a, b): string = + result = $a & $b + +test "if proc param types are not supplied, the params are assumed to be generic": + check concat(1, "test") == "1test" + check concat(1, 20) == "120" + check concat("foo", "bar") == "foobar" + +test "explicit param types can still be specified": + check concat[cstring, cstring]("x", "y") == "xy" + |