refactoring: suggest can import sigmatch for type matching

This commit is contained in:
Araq 2011-02-21 20:06:34 +01:00
commit fdde4d3a92
12 changed files with 445 additions and 266 deletions

View file

@ -51,16 +51,18 @@ type
TTabIter*{.final.} = object # consider all fields here private TTabIter*{.final.} = object # consider all fields here private
h*: THash # current hash h*: THash # current hash
proc InitTabIter*(ti: var TTabIter, tab: TStrTable): PSym proc InitTabIter*(ti: var TTabIter, tab: TStrTable): PSym
proc NextIter*(ti: var TTabIter, tab: TStrTable): PSym proc NextIter*(ti: var TTabIter, tab: TStrTable): PSym
# usage: # usage:
# var i: TTabIter; s: PSym; # var
# s := InitTabIter(i, table); # i: TTabIter
# while s <> nil do begin # s: PSym
# s = InitTabIter(i, table)
# while s != nil:
# ... # ...
# s := NextIter(i, table); # s = NextIter(i, table)
# end; #
type type
TIdentIter*{.final.} = object # iterator over all syms with the same identifier TIdentIter*{.final.} = object # iterator over all syms with the same identifier
h*: THash # current hash h*: THash # current hash

View file

@ -13,7 +13,7 @@ import
strutils, nhashes, lists, options, scanner, ast, astalgo, trees, treetab, strutils, nhashes, lists, options, scanner, ast, astalgo, trees, treetab,
wordrecg, ropes, msgs, os, condsyms, idents, rnimsyn, types, platform, math, wordrecg, ropes, msgs, os, condsyms, idents, rnimsyn, types, platform, math,
magicsys, pnimsyn, nversion, nimsets, semdata, evals, semfold, importer, magicsys, pnimsyn, nversion, nimsets, semdata, evals, semfold, importer,
procfind, lookups, rodread, pragmas, passes, suggest procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest
proc semPass*(): TPass proc semPass*(): TPass
# implementation # implementation
@ -34,10 +34,6 @@ proc isTopLevel(c: PContext): bool =
proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
result = newSym(kind, considerAcc(n), getCurrOwner()) result = newSym(kind, considerAcc(n), getCurrOwner())
result.info = n.info result.info = n.info
proc markUsed(n: PNode, s: PSym) =
incl(s.flags, sfUsed)
if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym
# identifier with visability # identifier with visability
@ -115,7 +111,7 @@ proc semMacroExpr(c: PContext, n: PNode, sym: PSym,
if semCheck: result = semAfterMacroCall(c, result, sym) if semCheck: result = semAfterMacroCall(c, result, sym)
dec(evalTemplateCounter) dec(evalTemplateCounter)
include seminst, sigmatch include seminst, semcall
proc CheckBool(t: PNode) = proc CheckBool(t: PNode) =
if (t.Typ == nil) or if (t.Typ == nil) or

98
rod/semcall.nim Normal file
View file

@ -0,0 +1,98 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module implements semantic checking for calls.
proc sameMethodDispatcher(a, b: PSym): bool =
result = false
if a.kind == skMethod and b.kind == skMethod:
var aa = lastSon(a.ast)
var bb = lastSon(b.ast)
if aa.kind == nkSym and bb.kind == nkSym and aa.sym == bb.sym:
result = true
proc semDirectCallWithBinding(c: PContext, n, f: PNode, filter: TSymKinds,
initialBinding: PNode): PNode =
var
o: TOverloadIter
x, y, z: TCandidate
#Message(n.info, warnUser, renderTree(n))
var sym = initOverloadIter(o, c, f)
result = nil
if sym == nil: return
initCandidate(x, sym, initialBinding)
initCandidate(y, sym, initialBinding)
while sym != nil:
if sym.kind in filter:
initCandidate(z, sym, initialBinding)
z.calleeSym = sym
matches(c, n, z)
if z.state == csMatch:
case x.state
of csEmpty, csNoMatch: x = z
of csMatch:
var cmp = cmpCandidates(x, z)
if cmp < 0: x = z # z is better than x
elif cmp == 0: y = z # z is as good as x
else: nil
sym = nextOverloadIter(o, c, f)
if x.state == csEmpty:
# no overloaded proc found
# do not generate an error yet; the semantic checking will check for
# an overloaded () operator
elif y.state == csMatch and cmpCandidates(x, y) == 0 and
not sameMethodDispatcher(x.calleeSym, y.calleeSym):
if x.state != csMatch:
InternalError(n.info, "x.state is not csMatch")
LocalError(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
getProcHeader(x.calleeSym), getProcHeader(y.calleeSym),
x.calleeSym.Name.s])
else:
# only one valid interpretation found:
markUsed(n, x.calleeSym)
if x.calleeSym.ast == nil:
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
if x.calleeSym.ast.sons[genericParamsPos].kind != nkEmpty:
# a generic proc!
x.calleeSym = generateInstance(c, x.calleeSym, x.bindings, n.info)
x.callee = x.calleeSym.typ
result = x.call
result.sons[0] = newSymNode(x.calleeSym)
result.typ = x.callee.sons[0]
proc semDirectCall(c: PContext, n: PNode, filter: TSymKinds): PNode =
# process the bindings once:
var initialBinding: PNode
var f = n.sons[0]
if f.kind == nkBracketExpr:
# fill in the bindings:
initialBinding = f
f = f.sons[0]
else:
initialBinding = nil
result = semDirectCallWithBinding(c, n, f, filter, initialBinding)
proc explictGenericInstantiation(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 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

View file

@ -166,6 +166,10 @@ proc makeRangeType(c: PContext, first, last: biggestInt,
result.n = n result.n = n
addSon(result, getSysType(tyInt)) # basetype of range addSon(result, getSysType(tyInt)) # basetype of range
proc markUsed*(n: PNode, s: PSym) =
incl(s.flags, sfUsed)
if sfDeprecated in s.flags: Message(n.info, warnDeprecated, s.name.s)
proc illFormedAst*(n: PNode) = proc illFormedAst*(n: PNode) =
GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments})) GlobalError(n.info, errIllFormedAstX, renderTree(n, {renderNoComments}))

View file

@ -12,8 +12,7 @@ const
ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128, ConstAbstractTypes = {tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
tyArrayConstr, tyTuple, tySet} tyArrayConstr, tyTuple, tySet}
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
semCheck: bool = true): PNode =
markUsed(n, s) markUsed(n, s)
pushInfoContext(n.info) pushInfoContext(n.info)
result = evalTemplate(c, n, s) result = evalTemplate(c, n, s)
@ -122,9 +121,9 @@ proc checkConvertible(info: TLineInfo, castDest, src: PType) =
proc isCastable(dst, src: PType): bool = proc isCastable(dst, src: PType): bool =
#const #const
# castableTypeKinds = {@set}[tyInt, tyPtr, tyRef, tyCstring, tyString, # castableTypeKinds = {tyInt, tyPtr, tyRef, tyCstring, tyString,
# tySequence, tyPointer, tyNil, tyOpenArray, # tySequence, tyPointer, tyNil, tyOpenArray,
# tyProc, tySet, tyEnum, tyBool, tyChar]; # tyProc, tySet, tyEnum, tyBool, tyChar}
var ds, ss: biggestInt var ds, ss: biggestInt
# this is very unrestrictive; cast is allowed if castDest.size >= src.size # this is very unrestrictive; cast is allowed if castDest.size >= src.size
ds = computeSize(dst) ds = computeSize(dst)
@ -232,7 +231,8 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
proc changeType(n: PNode, newType: PType) = proc changeType(n: PNode, newType: PType) =
case n.kind case n.kind
of nkCurly, nkBracket: of nkCurly, nkBracket:
for i in countup(0, sonsLen(n) - 1): changeType(n.sons[i], elemType(newType)) for i in countup(0, sonsLen(n) - 1):
changeType(n.sons[i], elemType(newType))
of nkPar: of nkPar:
if newType.kind != tyTuple: if newType.kind != tyTuple:
InternalError(n.info, "changeType: no tuple type for constructor") InternalError(n.info, "changeType: no tuple type for constructor")
@ -350,8 +350,6 @@ proc isAssignable(n: PNode): TAssignableResult =
result = isAssignable(n.sons[0]) result = isAssignable(n.sons[0])
of nkHiddenStdConv, nkHiddenSubConv, nkConv: of nkHiddenStdConv, nkHiddenSubConv, nkConv:
# Object and tuple conversions are still addressable, so we skip them # Object and tuple conversions are still addressable, so we skip them
#if skipPtrsGeneric(n.sons[1].typ).kind in [tyOpenArray,
# tyTuple, tyObject] then
if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}: if skipTypes(n.typ, abstractPtrs).kind in {tyOpenArray, tyTuple, tyObject}:
result = isAssignable(n.sons[1]) result = isAssignable(n.sons[1])
of nkHiddenDeref, nkDerefExpr: of nkHiddenDeref, nkDerefExpr:
@ -643,6 +641,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
n.sons[0] = semExprWithType(c, n.sons[0], {efAllowType} + flags) n.sons[0] = semExprWithType(c, n.sons[0], {efAllowType} + flags)
if gCmd == cmdSuggest:
suggestFieldAccess(c, n.sons[0])
var i = considerAcc(n.sons[1]) var i = considerAcc(n.sons[1])
var ty = n.sons[0].Typ var ty = n.sons[0].Typ
var f: PSym = nil var f: PSym = nil

View file

@ -7,35 +7,11 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# This module does the instantiation of generic procs and types. # This module does the instantiation of generic procs.
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
proc searchInstTypes(tab: TIdTable, key: PType): PType =
# returns nil if we need to declare this type
result = PType(IdTableGet(tab, key))
if (result == nil) and (tab.counter > 0):
# we have to do a slow linear search because types may need
# to be compared by their structure:
for h in countup(0, high(tab.data)):
var t = PType(tab.data[h].key)
if t != nil:
if key.containerId == t.containerID:
var match = true
for j in countup(0, sonsLen(t) - 1):
# XXX sameType is not really correct for nested generics?
if not sameType(t.sons[j], key.sons[j]):
match = false
break
if match:
return PType(tab.data[h].val)
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
result = t.kind in GenericTypes
proc containsGenericType(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)
proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable) = proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable) =
if (n.kind != nkGenericParams): if (n.kind != nkGenericParams):
@ -129,106 +105,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
c.p = oldP # restore c.p = oldP # restore
c.module = oldMod c.module = oldMod
dec(c.InstCounter) dec(c.InstCounter)
proc checkConstructedType(info: TLineInfo, t: PType) =
if (tfAcyclic in t.flags) and (skipTypes(t, abstractInst).kind != tyObject):
LocalError(info, errInvalidPragmaX, "acyclic")
elif computeSize(t) < 0:
LocalError(info, errIllegalRecursionInTypeX, typeToString(t))
elif (t.kind == tyVar) and (t.sons[0].kind == tyVar):
LocalError(info, errVarVarTypeNotAllowed)
type
TReplTypeVars{.final.} = object
c*: PContext
typeMap*: TIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym
info*: TLineInfo
proc ReplaceTypeVarsT(cl: var TReplTypeVars, t: PType): PType
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
if n != nil:
result = copyNode(n)
result.typ = ReplaceTypeVarsT(cl, n.typ)
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
nil
of nkSym:
result.sym = ReplaceTypeVarsS(cl, n.sym)
else:
var length = sonsLen(n)
if length > 0:
newSons(result, length)
for i in countup(0, length - 1):
result.sons[i] = ReplaceTypeVarsN(cl, n.sons[i])
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
if s == nil: return nil
result = PSym(idTableGet(cl.symMap, s))
if result == nil:
result = copySym(s, false)
incl(result.flags, sfFromGeneric)
idTablePut(cl.symMap, s, result)
result.typ = ReplaceTypeVarsT(cl, s.typ)
result.owner = s.owner
result.ast = ReplaceTypeVarsN(cl, s.ast)
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.typeMap, t))
if result == nil:
GlobalError(t.sym.info, errCannotInstantiateX, typeToString(t))
elif result.kind == tyGenericParam:
InternalError(cl.info, "substitution with generic parameter")
proc ReplaceTypeVarsT(cl: var TReplTypeVars, t: PType): PType =
var body, newbody, x, header: PType
result = t
if t == nil: return
case t.kind
of tyGenericParam:
result = lookupTypeVar(cl, t)
of tyGenericInvokation:
body = t.sons[0]
if body.kind != tyGenericBody: InternalError(cl.info, "no generic body")
header = nil
for i in countup(1, sonsLen(t) - 1):
if t.sons[i].kind == tyGenericParam:
x = lookupTypeVar(cl, t.sons[i])
if header == nil: header = copyType(t, t.owner, false)
header.sons[i] = x
else:
x = t.sons[i]
idTablePut(cl.typeMap, body.sons[i - 1], x)
if header == nil: header = t
result = searchInstTypes(gInstTypes, header)
if result != nil: return
result = newType(tyGenericInst, t.sons[0].owner)
for i in countup(0, sonsLen(t) - 1):
# if one of the params is not concrete, we cannot do anything
# but we already raised an error!
addSon(result, header.sons[i])
idTablePut(gInstTypes, header, result)
newbody = ReplaceTypeVarsT(cl, lastSon(body))
newbody.n = ReplaceTypeVarsN(cl, lastSon(body).n)
addSon(result, newbody)
#writeln(output, ropeToStr(Typetoyaml(newbody)));
checkConstructedType(cl.info, newbody)
of tyGenericBody:
InternalError(cl.info, "ReplaceTypeVarsT: tyGenericBody")
result = ReplaceTypeVarsT(cl, lastSon(t))
else:
if containsGenericType(t):
result = copyType(t, t.owner, false)
for i in countup(0, sonsLen(result) - 1):
result.sons[i] = ReplaceTypeVarsT(cl, result.sons[i])
result.n = ReplaceTypeVarsN(cl, result.n)
if result.Kind in GenericTypes:
LocalError(cl.info, errCannotInstantiateX, TypeToString(t, preferName))
#writeln(output, ropeToStr(Typetoyaml(result)))
#checkConstructedType(cl.info, result)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
var cl: TReplTypeVars var cl: TReplTypeVars
InitIdTable(cl.symMap) InitIdTable(cl.symMap)
@ -237,14 +114,3 @@ proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
cl.c = c cl.c = c
result = ReplaceTypeVarsT(cl, header) result = ReplaceTypeVarsT(cl, header)
proc generateTypeInstance(p: PContext, pt: TIdTable, arg: PNode,
t: PType): PType =
var cl: TReplTypeVars
InitIdTable(cl.symMap)
copyIdTable(cl.typeMap, pt)
cl.info = arg.info
cl.c = p
pushInfoContext(arg.info)
result = ReplaceTypeVarsT(cl, t)
popInfoContext()

View file

@ -245,7 +245,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
elif (sfMinus in allowed) and (v.id == ord(wMinus)): elif (sfMinus in allowed) and (v.id == ord(wMinus)):
incl(result.flags, sfMinus) incl(result.flags, sfMinus)
else: else:
GlobalError(n.sons[0].info, errInvalidVisibilityX, v.s) LocalError(n.sons[0].info, errInvalidVisibilityX, v.s)
else: else:
illFormedAst(n) illFormedAst(n)
else: else:

147
rod/semtypinst.nim Normal file
View file

@ -0,0 +1,147 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# This module does the instantiation of generic procs and types.
import ast, astalgo, msgs, types, semdata
proc checkConstructedType*(info: TLineInfo, t: PType) =
if (tfAcyclic in t.flags) and (skipTypes(t, abstractInst).kind != tyObject):
LocalError(info, errInvalidPragmaX, "acyclic")
elif computeSize(t) < 0:
LocalError(info, errIllegalRecursionInTypeX, typeToString(t))
elif (t.kind == tyVar) and (t.sons[0].kind == tyVar):
LocalError(info, errVarVarTypeNotAllowed)
proc containsGenericTypeIter(t: PType, closure: PObject): bool =
result = t.kind in GenericTypes
proc containsGenericType*(t: PType): bool =
result = iterOverType(t, containsGenericTypeIter, nil)
proc searchInstTypes(tab: TIdTable, key: PType): PType =
# returns nil if we need to declare this type
result = PType(IdTableGet(tab, key))
if (result == nil) and (tab.counter > 0):
# we have to do a slow linear search because types may need
# to be compared by their structure:
for h in countup(0, high(tab.data)):
var t = PType(tab.data[h].key)
if t != nil:
if key.containerId == t.containerID:
var match = true
for j in countup(0, sonsLen(t) - 1):
# XXX sameType is not really correct for nested generics?
if not sameType(t.sons[j], key.sons[j]):
match = false
break
if match:
return PType(tab.data[h].val)
type
TReplTypeVars* {.final.} = object
c*: PContext
typeMap*: TIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym
info*: TLineInfo
proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
if n != nil:
result = copyNode(n)
result.typ = ReplaceTypeVarsT(cl, n.typ)
case n.kind
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit:
nil
of nkSym:
result.sym = ReplaceTypeVarsS(cl, n.sym)
else:
var length = sonsLen(n)
if length > 0:
newSons(result, length)
for i in countup(0, length - 1):
result.sons[i] = ReplaceTypeVarsN(cl, n.sons[i])
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
if s == nil: return nil
result = PSym(idTableGet(cl.symMap, s))
if result == nil:
result = copySym(s, false)
incl(result.flags, sfFromGeneric)
idTablePut(cl.symMap, s, result)
result.typ = ReplaceTypeVarsT(cl, s.typ)
result.owner = s.owner
result.ast = ReplaceTypeVarsN(cl, s.ast)
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.typeMap, t))
if result == nil:
GlobalError(t.sym.info, errCannotInstantiateX, typeToString(t))
elif result.kind == tyGenericParam:
InternalError(cl.info, "substitution with generic parameter")
proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
var body, newbody, x, header: PType
result = t
if t == nil: return
case t.kind
of tyGenericParam:
result = lookupTypeVar(cl, t)
of tyGenericInvokation:
body = t.sons[0]
if body.kind != tyGenericBody: InternalError(cl.info, "no generic body")
header = nil
for i in countup(1, sonsLen(t) - 1):
if t.sons[i].kind == tyGenericParam:
x = lookupTypeVar(cl, t.sons[i])
if header == nil: header = copyType(t, t.owner, false)
header.sons[i] = x
else:
x = t.sons[i]
idTablePut(cl.typeMap, body.sons[i - 1], x)
if header == nil: header = t
result = searchInstTypes(gInstTypes, header)
if result != nil: return
result = newType(tyGenericInst, t.sons[0].owner)
for i in countup(0, sonsLen(t) - 1):
# if one of the params is not concrete, we cannot do anything
# but we already raised an error!
addSon(result, header.sons[i])
idTablePut(gInstTypes, header, result)
newbody = ReplaceTypeVarsT(cl, lastSon(body))
newbody.n = ReplaceTypeVarsN(cl, lastSon(body).n)
addSon(result, newbody)
#writeln(output, ropeToStr(Typetoyaml(newbody)));
checkConstructedType(cl.info, newbody)
of tyGenericBody:
InternalError(cl.info, "ReplaceTypeVarsT: tyGenericBody")
result = ReplaceTypeVarsT(cl, lastSon(t))
else:
if containsGenericType(t):
result = copyType(t, t.owner, false)
for i in countup(0, sonsLen(result) - 1):
result.sons[i] = ReplaceTypeVarsT(cl, result.sons[i])
result.n = ReplaceTypeVarsN(cl, result.n)
if result.Kind in GenericTypes:
LocalError(cl.info, errCannotInstantiateX, TypeToString(t, preferName))
#writeln(output, ropeToStr(Typetoyaml(result)))
#checkConstructedType(cl.info, result)
proc generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
t: PType): PType =
var cl: TReplTypeVars
InitIdTable(cl.symMap)
copyIdTable(cl.typeMap, pt)
cl.info = arg.info
cl.c = p
pushInfoContext(arg.info)
result = ReplaceTypeVarsT(cl, t)
popInfoContext()

View file

@ -10,24 +10,28 @@
# This module implements the signature matching for resolving # This module implements the signature matching for resolving
# the call to overloaded procs, generic procs and operators. # the call to overloaded procs, generic procs and operators.
import
ast, astalgo, semdata, types, msgs, rnimsyn, lookups, semtypinst,
magicsys
type type
TCandidateState = enum TCandidateState* = enum
csEmpty, csMatch, csNoMatch csEmpty, csMatch, csNoMatch
TCandidate{.final.} = object TCandidate* {.final.} = object
exactMatches: int exactMatches: int
subtypeMatches: int subtypeMatches: int
intConvMatches: int # conversions to int are not as expensive intConvMatches: int # conversions to int are not as expensive
convMatches: int convMatches: int
genericMatches: int genericMatches: int
state: TCandidateState state*: TCandidateState
callee: PType # may not be nil! callee*: PType # may not be nil!
calleeSym: PSym # may be nil calleeSym*: PSym # may be nil
call: PNode # modified call call*: PNode # modified call
bindings: TIdTable # maps sym-ids to types bindings*: TIdTable # maps sym-ids to types
baseTypeMatch: bool # needed for conversions from T to openarray[T] baseTypeMatch: bool # needed for conversions from T to openarray[T]
# for example # for example
TTypeRelation = enum # order is important! TTypeRelation* = enum # order is important!
isNone, isConvertible, isIntConv, isSubtype, isGeneric, isEqual isNone, isConvertible, isIntConv, isSubtype, isGeneric, isEqual
proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} = proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
@ -41,12 +45,12 @@ proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
c.call = nil c.call = nil
c.baseTypeMatch = false c.baseTypeMatch = false
proc initCandidate(c: var TCandidate, callee: PType) = proc initCandidate*(c: var TCandidate, callee: PType) =
initCandidateAux(c, callee) initCandidateAux(c, callee)
c.calleeSym = nil c.calleeSym = nil
initIdTable(c.bindings) initIdTable(c.bindings)
proc initCandidate(c: var TCandidate, callee: PSym, binding: PNode) = proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode) =
initCandidateAux(c, callee.typ) initCandidateAux(c, callee.typ)
c.calleeSym = callee c.calleeSym = callee
initIdTable(c.bindings) initIdTable(c.bindings)
@ -70,7 +74,7 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) =
a.baseTypeMatch = b.baseTypeMatch a.baseTypeMatch = b.baseTypeMatch
copyIdTable(a.bindings, b.bindings) copyIdTable(a.bindings, b.bindings)
proc cmpCandidates(a, b: TCandidate): int = proc cmpCandidates*(a, b: TCandidate): int =
result = a.exactMatches - b.exactMatches result = a.exactMatches - b.exactMatches
if result != 0: return if result != 0: return
result = a.genericMatches - b.genericMatches result = a.genericMatches - b.genericMatches
@ -88,7 +92,7 @@ proc writeMatches(c: TCandidate) =
Writeln(stdout, "intconv matches: " & $c.intConvMatches) Writeln(stdout, "intconv matches: " & $c.intConvMatches)
Writeln(stdout, "generic matches: " & $c.genericMatches) Writeln(stdout, "generic matches: " & $c.genericMatches)
proc getNotFoundError(c: PContext, n: PNode): string = proc getNotFoundError*(c: PContext, n: PNode): string =
# Gives a detailed error message; this is separated from semDirectCall, # Gives a detailed error message; this is separated from semDirectCall,
# as semDirectCall is already pretty slow (and we need this information only # as semDirectCall is already pretty slow (and we need this information only
# in case of an error). # in case of an error).
@ -425,7 +429,7 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
else: nil else: nil
else: internalError("typeRel(" & $f.kind & ')') else: internalError("typeRel(" & $f.kind & ')')
proc cmpTypes(f, a: PType): TTypeRelation = proc cmpTypes*(f, a: PType): TTypeRelation =
var mapping: TIdTable var mapping: TIdTable
InitIdTable(mapping) InitIdTable(mapping)
result = typeRel(mapping, f, a) result = typeRel(mapping, f, a)
@ -552,7 +556,7 @@ proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
markUsed(arg, arg.sons[best].sym) markUsed(arg, arg.sons[best].sym)
result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best]) result = ParamTypesMatchAux(c, m, f, arg.sons[best].typ, arg.sons[best])
proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = proc IndexTypesMatch*(c: PContext, f, a: PType, arg: PNode): PNode =
var m: TCandidate var m: TCandidate
initCandidate(m, f) initCandidate(m, f)
result = paramTypesMatch(c, m, f, a, arg) result = paramTypesMatch(c, m, f, a, arg)
@ -561,7 +565,7 @@ proc setSon(father: PNode, at: int, son: PNode) =
if sonsLen(father) <= at: setlen(father.sons, at + 1) if sonsLen(father) <= at: setlen(father.sons, at + 1)
father.sons[at] = son father.sons[at] = son
proc matches(c: PContext, n: PNode, m: var TCandidate) = proc matches*(c: PContext, n: PNode, m: var TCandidate) =
var f = 1 # iterates over formal parameters var f = 1 # iterates over formal parameters
var a = 1 # iterates over the actual given arguments var a = 1 # iterates over the actual given arguments
m.state = csMatch # until proven otherwise m.state = csMatch # until proven otherwise
@ -670,90 +674,3 @@ proc matches(c: PContext, n: PNode, m: var TCandidate) =
setSon(m.call, formal.position + 1, copyTree(formal.ast)) setSon(m.call, formal.position + 1, copyTree(formal.ast))
inc(f) inc(f)
proc sameMethodDispatcher(a, b: PSym): bool =
result = false
if a.kind == skMethod and b.kind == skMethod:
var aa = lastSon(a.ast)
var bb = lastSon(b.ast)
if aa.kind == nkSym and bb.kind == nkSym and aa.sym == bb.sym:
result = true
proc semDirectCallWithBinding(c: PContext, n, f: PNode, filter: TSymKinds,
initialBinding: PNode): PNode =
var
o: TOverloadIter
x, y, z: TCandidate
#Message(n.info, warnUser, renderTree(n))
var sym = initOverloadIter(o, c, f)
result = nil
if sym == nil: return
initCandidate(x, sym, initialBinding)
initCandidate(y, sym, initialBinding)
while sym != nil:
if sym.kind in filter:
initCandidate(z, sym, initialBinding)
z.calleeSym = sym
matches(c, n, z)
if z.state == csMatch:
case x.state
of csEmpty, csNoMatch: x = z
of csMatch:
var cmp = cmpCandidates(x, z)
if cmp < 0: x = z # z is better than x
elif cmp == 0: y = z # z is as good as x
else: nil
sym = nextOverloadIter(o, c, f)
if x.state == csEmpty:
# no overloaded proc found
# do not generate an error yet; the semantic checking will check for
# an overloaded () operator
elif y.state == csMatch and cmpCandidates(x, y) == 0 and
not sameMethodDispatcher(x.calleeSym, y.calleeSym):
if x.state != csMatch:
InternalError(n.info, "x.state is not csMatch")
LocalError(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
getProcHeader(x.calleeSym), getProcHeader(y.calleeSym),
x.calleeSym.Name.s])
else:
# only one valid interpretation found:
markUsed(n, x.calleeSym)
if x.calleeSym.ast == nil:
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
if x.calleeSym.ast.sons[genericParamsPos].kind != nkEmpty:
# a generic proc!
x.calleeSym = generateInstance(c, x.calleeSym, x.bindings, n.info)
x.callee = x.calleeSym.typ
result = x.call
result.sons[0] = newSymNode(x.calleeSym)
result.typ = x.callee.sons[0]
proc semDirectCall(c: PContext, n: PNode, filter: TSymKinds): PNode =
# process the bindings once:
var initialBinding: PNode
var f = n.sons[0]
if f.kind == nkBracketExpr:
# fill in the bindings:
initialBinding = f
f = f.sons[0]
else:
initialBinding = nil
result = semDirectCallWithBinding(c, n, f, filter, initialBinding)
proc explictGenericInstantiation(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 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

View file

@ -9,7 +9,7 @@
## This file implements features required for IDE support. ## This file implements features required for IDE support.
import scanner, ast, astalgo, semdata, msgs, types import scanner, ast, astalgo, semdata, msgs, types, sigmatch
const const
sep = '\t' sep = '\t'
@ -33,23 +33,83 @@ proc SymToStr(s: PSym, isLocal: bool): string =
result.add(sep) result.add(sep)
result.add($ToColumn(s.info)) result.add($ToColumn(s.info))
proc suggestSym(s: PSym): bool {.inline.} = proc filterSym(s: PSym): bool {.inline.} =
result = s.name.s[0] in scanner.SymChars result = s.name.s[0] in scanner.SymChars
proc suggestField(s: PSym) =
if filterSym(s):
MessageOut(SymToStr(s, isLocal=true))
proc suggestExpr*(c: PContext, n: PNode) = proc suggestExpr*(c: PContext, n: PNode) =
if not msgs.inCheckpoint(n.info): return if not msgs.inCheckpoint(n.info): return
for i in countdown(c.tab.tos-1, 0): for i in countdown(c.tab.tos-1, 0):
for it in items(c.tab.stack[i]): for it in items(c.tab.stack[i]):
if suggestSym(it): if filterSym(it):
MessageOut(SymToStr(it, i > ModuleTablePos)) MessageOut(SymToStr(it, isLocal = i > ModuleTablePos))
quit(0) quit(0)
proc suggestStmt*(c: PContext, n: PNode) = proc suggestStmt*(c: PContext, n: PNode) =
suggestExpr(c, n) suggestExpr(c, n)
proc suggestSymList(list: PNode) =
for i in countup(0, sonsLen(list) - 1):
if list.sons[i].kind != nkSym: InternalError(list.info, "getSymFromList")
suggestField(list.sons[i].sym)
proc suggestObject(n: PNode) =
case n.kind
of nkRecList:
for i in countup(0, sonsLen(n) - 1): suggestObject(n.sons[i])
of nkRecCase:
var L = sonsLen(n)
if L > 0:
suggestObject(n.sons[0])
for i in countup(1, L-1):
suggestObject(lastSon(n.sons[i]))
of nkSym: suggestField(n.sym)
else: nil
proc suggestOperations(c: PContext, n: PNode, typ: PType) =
nil
proc suggestFieldAccess*(c: PContext, n: PNode) = proc suggestFieldAccess*(c: PContext, n: PNode) =
suggestExpr(c, n) # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
# XXX provide a better implementation based on n[0].typ # ``myObj``.
var typ = n.Typ
if typ == nil:
# a module symbol has no type for example:
if n.kind == nkSym and n.sym.kind == skModule:
if n.sym == c.module:
# all symbols accessible, because we are in the current module:
for it in items(c.tab.stack[ModuleTablePos]):
if filterSym(it): MessageOut(SymToStr(it, isLocal=false))
else:
for it in items(n.sym.tab):
if filterSym(it): MessageOut(SymToStr(it, isLocal=false))
else:
# fallback:
suggestExpr(c, n)
elif typ.kind == tyEnum:
# look up if the identifier belongs to the enum:
var t = typ
while t != nil:
suggestSymList(t.n)
t = t.sons[0]
suggestOperations(c, n, typ)
else:
typ = skipTypes(typ, {tyGenericInst, tyVar, tyPtr, tyRef})
if typ.kind == tyObject:
var t = typ
while true:
suggestObject(t.n)
if t.sons[0] == nil: break
t = skipTypes(t.sons[0], {tyGenericInst})
suggestOperations(c, n, typ)
elif typ.kind == tyTuple and typ.n != nil:
suggestSymList(typ.n)
suggestOperations(c, n, typ)
else:
# fallback:
suggestExpr(c, n)

57
tests/accept/run/tgenerics1.nim Executable file
View file

@ -0,0 +1,57 @@
discard """
output: "256 100"
"""
# A min-heap.
type
TNode[T] = tuple[priority: int, data: T]
TBinHeap[T] = object
heap: seq[TNode[T]]
last: int
PBinHeap[T] = ref TBinHeap[T]
proc newBinHeap*[T](heap: var PBinHeap[T], size: int) =
new(heap)
heap.last = 0
newSeq(heap.seq, size)
when false:
proc parent(elem: int): int =
return (elem-1) div 2
proc siftUp[T](heap: PBinHeap[T], elem: int) =
var idx = elem
while idx != 0:
var p = parent(idx)
if heap.heap[idx] < heap.heap[p]:
var tmp = heap.heap[idx]
heap.heap[idx] = heap.heap[p]
heap.heap[p] = tmp
idx = p
else:
break
proc add*[T](heap: PBinHeap[T], priority: int, data: T) =
var node: TNode
new(node)
node.priority = int
node.data = data
heap.heap[heap.last] = node
siftUp(heap, heap.last)
inc(heap.last)
proc print*[T](heap: PBinHeap[T]) =
for i in countup(0, heap.last):
echo($heap.heap[i])
var
heap: PBinHeap[int]
newBinHeap(heap, 256)
add(heap, 1, 100)
print(heap)

View file

@ -0,0 +1,32 @@
discard """
output: "hellow"
"""
import
macros, strutils
macro outterMacro*(n: stmt): stmt =
var j : string = "hi"
proc innerProc(i: int): string =
echo "Using arg ! " & n.repr
result = "Got: '" & $n.kind & "' " & $j
if n.kind != TNimrodNodeKind.nnkMacroStmt:
error("Macro " & n[0].repr & " requires a block.")
var callNode = n[0]
expectKind(callNode, TNimrodNodeKind.nnkCall)
if callNode.len != 2 or callNode[1].kind != TNimrodNodeKind.nnkIdent:
error("Macro " & callNode.repr &
" requires the ident passed as parameter (eg: " & callNode.repr &
"(the_name_you_want)): statements.")
result = newNimNode(TNimrodNodeKind.nnkStmtList)
var ass : PNimrodNode = newNimNode(TNimrodNodeKind.nnkAsgn)
ass.add(newIdentNode(callNode[1].ident))
ass.add(newStrLitNode(innerProc(4)))
result.add(ass)
var str: string
outterMacro(str):
"hellow"
echo str