expr params implemented for procs; paving the way for type classes
This commit is contained in:
parent
4f22326b24
commit
3a5cf3d63a
6 changed files with 88 additions and 29 deletions
|
|
@ -8,6 +8,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
# This module implements the instantiation of generic procs.
|
# This module implements the instantiation of generic procs.
|
||||||
|
# included from sem.nim
|
||||||
|
|
||||||
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
|
||||||
entry: var TInstantiatedSymbol) =
|
entry: var TInstantiatedSymbol) =
|
||||||
|
|
@ -106,6 +107,35 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
|
||||||
s.ast.sons[genericParamsPos].kind == nkEmpty:
|
s.ast.sons[genericParamsPos].kind == nkEmpty:
|
||||||
c.threadEntries.add(s)
|
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,
|
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
info: TLineInfo): PSym =
|
info: TLineInfo): PSym =
|
||||||
# generates an instantiated proc
|
# generates an instantiated proc
|
||||||
|
|
@ -133,12 +163,14 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry)
|
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry)
|
||||||
n.sons[genericParamsPos] = ast.emptyNode
|
n.sons[genericParamsPos] = ast.emptyNode
|
||||||
# semantic checking for the parameters:
|
# semantic checking for the parameters:
|
||||||
if n.sons[paramsPos].kind != nkEmpty:
|
if n.sons[paramsPos].kind != nkEmpty:
|
||||||
removeDefaultParamValues(n.sons[ParamsPos])
|
if false and nimdbg:
|
||||||
semParamList(c, n.sons[ParamsPos], nil, result)
|
result.typ = applyConcreteTypesToSig(fn, entry.concreteTypes)
|
||||||
# XXX: obsoleted - happens in semParamList #
|
addParams(c, result.typ.n, fn.kind)
|
||||||
# addParams(c, result.typ.n)
|
else:
|
||||||
else:
|
removeDefaultParamValues(n.sons[ParamsPos])
|
||||||
|
semParamList(c, n.sons[ParamsPos], nil, result)
|
||||||
|
else:
|
||||||
result.typ = newTypeS(tyProc, c)
|
result.typ = newTypeS(tyProc, c)
|
||||||
addSon(result.typ, nil)
|
addSon(result.typ, nil)
|
||||||
result.typ.callConv = fn.typ.callConv
|
result.typ.callConv = fn.typ.callConv
|
||||||
|
|
|
||||||
|
|
@ -641,8 +641,6 @@ proc semLambda(c: PContext, n: PNode): PNode =
|
||||||
illFormedAst(n) # process parameters:
|
illFormedAst(n) # process parameters:
|
||||||
if n.sons[paramsPos].kind != nkEmpty:
|
if n.sons[paramsPos].kind != nkEmpty:
|
||||||
semParamList(c, n.sons[ParamsPos], nil, s)
|
semParamList(c, n.sons[ParamsPos], nil, s)
|
||||||
# XXX: obsoleted - happens in semParamList
|
|
||||||
# addParams(c, s.typ.n)
|
|
||||||
ParamsTypeCheck(c, s.typ)
|
ParamsTypeCheck(c, s.typ)
|
||||||
else:
|
else:
|
||||||
s.typ = newTypeS(tyProc, c)
|
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:
|
# we have a list of implicit type parameters:
|
||||||
n.sons[genericParamsPos] = gp
|
n.sons[genericParamsPos] = gp
|
||||||
# check for semantics again:
|
# check for semantics again:
|
||||||
semParamList(c, n.sons[ParamsPos], nil, s)
|
# semParamList(c, n.sons[ParamsPos], nil, s)
|
||||||
else:
|
else:
|
||||||
s.typ = newTypeS(tyProc, c)
|
s.typ = newTypeS(tyProc, c)
|
||||||
addSon(s.typ, nil)
|
addSon(s.typ, nil)
|
||||||
|
|
|
||||||
|
|
@ -520,6 +520,9 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
|
||||||
else:
|
else:
|
||||||
addDecl(c, param)
|
addDecl(c, param)
|
||||||
|
|
||||||
|
proc isTypeClass(c: PContext, t: PType): bool =
|
||||||
|
return t.kind in {tyExpr}
|
||||||
|
|
||||||
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
prev: PType, kind: TSymKind): PType =
|
prev: PType, kind: TSymKind): PType =
|
||||||
var
|
var
|
||||||
|
|
@ -536,7 +539,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
addSon(result.n, res)
|
addSon(result.n, res)
|
||||||
var check = initIntSet()
|
var check = initIntSet()
|
||||||
var counter = 0
|
var counter = 0
|
||||||
for i in countup(1, sonsLen(n)-1):
|
for i in countup(1, n.len - 1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if a.kind != nkIdentDefs: IllFormedAst(a)
|
if a.kind != nkIdentDefs: IllFormedAst(a)
|
||||||
checkMinSonsLen(a, 3)
|
checkMinSonsLen(a, 3)
|
||||||
|
|
@ -569,6 +572,21 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
for j in countup(0, length-3):
|
for j in countup(0, length-3):
|
||||||
var arg = newSymS(skParam, a.sons[j], c)
|
var arg = newSymS(skParam, a.sons[j], c)
|
||||||
arg.typ = typ
|
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
|
arg.position = counter
|
||||||
inc(counter)
|
inc(counter)
|
||||||
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
|
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 = newSymS(skType, a.sons[j], c)
|
||||||
s.typ = newTypeS(tyGenericParam, c)
|
s.typ = newTypeS(tyGenericParam, c)
|
||||||
of tyExpr:
|
of tyExpr:
|
||||||
|
echo "GENERIC EXPR ", a.info.toFileLineCol
|
||||||
# not a type param, but an expression
|
# not a type param, but an expression
|
||||||
|
# proc foo[x: expr](bar: int) what is this?
|
||||||
s = newSymS(skGenericParam, a.sons[j], c)
|
s = newSymS(skGenericParam, a.sons[j], c)
|
||||||
s.typ = typ
|
s.typ = typ
|
||||||
else:
|
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 = newSymS(skType, a.sons[j], c)
|
||||||
s.typ = typ
|
s.typ = typ
|
||||||
if def.kind != nkEmpty: s.ast = def
|
if def.kind != nkEmpty: s.ast = def
|
||||||
|
|
|
||||||
|
|
@ -463,14 +463,8 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
else:
|
else:
|
||||||
result = typeRel(mapping, x, a) # check if it fits
|
result = typeRel(mapping, x, a) # check if it fits
|
||||||
of tyExpr, tyStmt, tyTypeDesc:
|
of tyExpr, tyStmt, tyTypeDesc:
|
||||||
if a.kind == f.kind:
|
result = isGeneric
|
||||||
result = isEqual
|
|
||||||
else:
|
|
||||||
case a.kind
|
|
||||||
of tyExpr, tyStmt, tyTypeDesc: result = isGeneric
|
|
||||||
of tyNil: result = isSubtype
|
|
||||||
else: nil
|
|
||||||
else: internalError("typeRel(" & $f.kind & ')')
|
else: internalError("typeRel(" & $f.kind & ')')
|
||||||
|
|
||||||
proc cmpTypes*(f, a: PType): TTypeRelation =
|
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,
|
proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||||
arg, argOrig: PNode): PNode =
|
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)
|
var r = typeRel(m.bindings, f, a)
|
||||||
case r
|
case r
|
||||||
of isConvertible:
|
of isConvertible:
|
||||||
|
|
@ -528,14 +519,17 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||||
of isSubtype:
|
of isSubtype:
|
||||||
inc(m.subtypeMatches)
|
inc(m.subtypeMatches)
|
||||||
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
|
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
|
||||||
of isGeneric:
|
of isGeneric:
|
||||||
inc(m.genericMatches)
|
inc(m.genericMatches)
|
||||||
result = copyTree(arg)
|
if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
|
||||||
result.typ = getInstantiatedType(c, arg, m, f)
|
result = argOrig
|
||||||
# BUG: f may not be the right key!
|
else:
|
||||||
if skipTypes(result.typ, abstractVar).kind in {tyTuple}:
|
result = copyTree(arg)
|
||||||
result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
|
result.typ = getInstantiatedType(c, arg, m, f)
|
||||||
# BUGFIX: use ``result.typ`` and not `f` here
|
# 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:
|
of isEqual:
|
||||||
inc(m.exactMatches)
|
inc(m.exactMatches)
|
||||||
result = copyTree(arg)
|
result = copyTree(arg)
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
import uclosures, utemplates
|
import uclosures, utemplates, uexpr
|
||||||
|
|
||||||
|
|
|
||||||
13
tests/run/uexpr.nim
Normal file
13
tests/run/uexpr.nim
Normal 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"
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue