expr params implemented for procs; paving the way for type classes

This commit is contained in:
Zahary Karadjov
2012-03-21 23:48:03 +02:00
parent 4f22326b24
commit 3a5cf3d63a
6 changed files with 88 additions and 29 deletions

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,2 +1,2 @@
import uclosures, utemplates
import uclosures, utemplates, uexpr

13
tests/run/uexpr.nim Normal file
View File

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