various bugfixes for generics; added generic sort proc

This commit is contained in:
Araq
2011-03-03 02:01:22 +01:00
parent f93ca8e42a
commit e424e13bd9
18 changed files with 392 additions and 113 deletions

View File

@@ -750,6 +750,12 @@ proc newSymNode(sym: PSym): PNode =
result.typ = sym.typ
result.info = sym.info
proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
result = newNode(nkSym)
result.sym = sym
result.typ = sym.typ
result.info = info
proc newNodeI(kind: TNodeKind, info: TLineInfo): PNode =
result = newNode(kind)
result.info = info
@@ -869,6 +875,11 @@ proc len*(n: PNode): int {.inline.} =
if isNil(n.sons): result = 0
else: result = len(n.sons)
proc safeLen*(n: PNode): int {.inline.} =
## works even for leaves.
if n.kind in {nkNone..nkNilLit} or isNil(n.sons): result = 0
else: result = len(n.sons)
proc add*(father, son: PNode) =
assert son != nil
if isNil(father.sons): father.sons = @[]

View File

@@ -79,20 +79,42 @@ proc semDirectCall(c: PContext, n: PNode, filter: TSymKinds): PNode =
initialBinding = nil
result = semDirectCallWithBinding(c, n, f, filter, initialBinding)
proc explictGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
proc explicitGenericInstError(n: PNode): PNode =
LocalError(n.info, errCannotInstantiateX, renderTree(n))
result = n
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
assert n.kind == nkBracketExpr
for i in 1..sonsLen(n)-1:
n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
# we cannot check for the proper number of type parameters because in
# `f[a,b](x, y)` `f` is not resolved yet properly.
# XXX: BUG this should be checked somehow!
assert n.sons[0].kind == nkSym
var s = s
var a = n.sons[0]
if a.kind == nkSym:
# common case; check the only candidate has the right
# number of generic type parameters:
if safeLen(s.ast.sons[genericParamsPos]) != n.len-1:
return explicitGenericInstError(n)
elif a.kind == nkSymChoice:
# choose the generic proc with the proper number of type parameters.
# XXX I think this could be improved by reusing sigmatch.ParamTypesMatch.
# It's good enough for now.
var candidateCount = 0
for i in countup(0, len(a)-1):
var candidate = a.sons[i].sym
if candidate.kind in {skProc, skMethod, skConverter, skIterator}:
# if suffices that the candidate has the proper number of generic
# type parameters:
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
s = candidate
inc(candidateCount)
if candidateCount != 1: return explicitGenericInstError(n)
else:
assert false
var x: TCandidate
initCandidate(x, s, n)
var newInst = generateInstance(c, s, x.bindings, n.info)
markUsed(n, s)
result = newSymNode(newInst)
result.info = n.info
result = newSymNode(newInst, n.info)

View File

@@ -61,24 +61,22 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
if s.typ.kind in ConstAbstractTypes:
result = copyTree(s.ast)
result.typ = s.typ
result.info = n.info
else:
result = newSymNode(s)
result.info = n.info
result = newSymNode(s, n.info)
of skMacro: result = semMacroExpr(c, n, s)
of skTemplate: result = semTemplateExpr(c, n, s)
of skVar:
markUsed(n, s)
# if a proc accesses a global variable, it is not side effect free:
if sfGlobal in s.flags: incl(c.p.owner.flags, sfSideEffect)
result = newSymNode(s)
result.info = n.info
result = newSymNode(s, n.info)
of skGenericParam:
if s.ast == nil: InternalError(n.info, "no default for")
result = semExpr(c, s.ast)
else:
markUsed(n, s)
result = newSymNode(s)
result.info = n.info
result = newSymNode(s, n.info)
proc checkConversionBetweenObjects(info: TLineInfo, castDest, src: PType) =
var diff = inheritanceDiff(castDest, src)
@@ -645,23 +643,22 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ty = n.sons[0].Typ
var f: PSym = nil
result = nil
if ty.kind == tyEnum:
# look up if the identifier belongs to the enum:
while ty != nil:
f = getSymFromList(ty.n, i)
if f != nil: break
ty = ty.sons[0] # enum inheritance
if f != nil:
result = newSymNode(f)
result.info = n.info
result.typ = ty
markUsed(n, f)
else:
GlobalError(n.sons[1].info, errEnumHasNoValueX, i.s)
return
elif not (efAllowType in flags) and isTypeExpr(n.sons[0]):
GlobalError(n.sons[0].info, errATypeHasNoValue)
return
if isTypeExpr(n.sons[0]):
if ty.kind == tyEnum:
# look up if the identifier belongs to the enum:
while ty != nil:
f = getSymFromList(ty.n, i)
if f != nil: break
ty = ty.sons[0] # enum inheritance
if f != nil:
result = newSymNode(f)
result.info = n.info
result.typ = ty
markUsed(n, f)
return
elif efAllowType notin flags:
GlobalError(n.sons[0].info, errATypeHasNoValue)
return
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef})
var check: PNode = nil
if ty.kind == tyObject:
@@ -1027,7 +1024,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}:
# type parameters: partial generic specialization
result = explictGenericInstantiation(c, n, s)
n.sons[0] = semSym(c, n.sons[0], s, flags)
result = explicitGenericInstantiation(c, n, s)
else:
result = semArrayAccess(c, n, flags)
of nkPragmaExpr:

View File

@@ -40,13 +40,15 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
of skMacro:
result = semMacroExpr(c, n, s, false)
of skGenericParam:
result = newSymNode(s)
result = newSymNode(s, n.info)
of skParam:
result = n
of skType:
if (s.typ != nil) and (s.typ.kind != tyGenericParam): result = newSymNode(s)
else: result = n
else: result = newSymNode(s)
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
result = newSymNode(s, n.info)
else:
result = n
else: result = newSymNode(s, n.info)
proc getIdentNode(n: PNode): PNode =
case n.kind
@@ -101,12 +103,12 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
of skProc, skMethod, skIterator, skConverter:
n.sons[0] = symChoice(c, n.sons[0], s)
of skGenericParam:
n.sons[0] = newSymNode(s)
n.sons[0] = newSymNode(s, n.sons[0].info)
of skType:
# bad hack for generics:
if (s.typ != nil) and (s.typ.kind != tyGenericParam):
n.sons[0] = newSymNode(s)
else: n.sons[0] = newSymNode(s)
n.sons[0] = newSymNode(s, n.sons[0].info)
else: n.sons[0] = newSymNode(s, n.sons[0].info)
for i in countup(1, sonsLen(n) - 1):
n.sons[i] = semGenericStmt(c, n.sons[i], flags)
of nkMacroStmt:
@@ -221,9 +223,9 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
if (a.kind != nkIdentDefs): IllFormedAst(a)
checkMinSonsLen(a, 3)
L = sonsLen(a)
a.sons[L - 1] = semGenericStmt(c, a.sons[L - 2], {withinTypeDesc})
a.sons[L - 1] = semGenericStmt(c, a.sons[L - 1])
for j in countup(0, L - 3):
a.sons[L-2] = semGenericStmt(c, a.sons[L-2], {withinTypeDesc})
a.sons[L-1] = semGenericStmt(c, a.sons[L-1])
for j in countup(0, L-3):
addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[j]), c))
of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef,
nkIteratorDef, nkLambda:

View File

@@ -522,11 +522,15 @@ proc SemTypeSection(c: PContext, n: PNode): PNode =
InternalError(a.info, "semTypeSection: containerID")
s.typ.containerID = getID()
a.sons[1] = semGenericParamList(c, a.sons[1], s.typ)
addSon(s.typ, nil) # to be filled out later
# we fill it out later. For magic generics like 'seq', it won't be filled
# so we use tyEmpty instead of nil to not crash for strange conversions
# like: mydata.seq
addSon(s.typ, newTypeS(tyEmpty, c))
s.ast = a
body = semTypeNode(c, a.sons[2], nil)
if body != nil: body.sym = s
s.typ.sons[sonsLen(s.typ) - 1] = body #debug(s.typ);
s.typ.sons[sonsLen(s.typ) - 1] = body
popOwner()
closeScope(c.tab)
elif a.sons[2].kind != nkEmpty:

View File

@@ -488,7 +488,8 @@ proc paramType(c: PContext, n, genericParams: PNode, cl: var TIntSet): PType =
result = addTypeVarsOfGenericBody(c, result, genericParams, cl)
#if result.kind == tyGenericInvokation: debug(result)
proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType): PType =
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
prev: PType): PType =
var
def, res: PNode
typ: PType
@@ -515,12 +516,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType): PType =
if a.sons[length - 1].kind != nkEmpty:
def = semExprWithType(c, a.sons[length - 1])
# check type compability between def.typ and typ:
if typ != nil:
if cmpTypes(typ, def.typ) < isConvertible:
typeMismatch(a.sons[length - 1], typ, def.typ)
def = fitNode(c, typ, def)
else:
if typ == nil:
typ = def.typ
elif def != nil and def.typ != nil and def.typ.kind != tyNone:
# example code that triggers it:
# proc sort[T](cmp: proc(a, b: T): int = cmp)
def = fitNode(c, typ, def)
else:
def = ast.emptyNode
for j in countup(0, length - 3):

View File

@@ -130,8 +130,11 @@ proc concreteType(mapping: TIdTable, t: PType): PType =
result = t
while true:
result = PType(idTableGet(mapping, t))
if result == nil: InternalError("lookup failed")
if result.kind != tyGenericParam: break
if result == nil:
break # it's ok, no match
# example code that triggers it:
# proc sort[T](cmp: proc(a, b: T): int = cmp)
if result.kind != tyGenericParam: break
else:
result = t # Note: empty is valid here

View File

@@ -123,7 +123,7 @@ proc suggestFieldAccess(c: PContext, n: PNode) =
else:
# fallback:
suggestEverything(c, n)
elif typ.kind == tyEnum:
elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType:
# look up if the identifier belongs to the enum:
var t = typ
while t != nil: