merged branch overloading-for-macros

This commit is contained in:
Zahary Karadjov
2012-03-19 12:06:38 +02:00
39 changed files with 366 additions and 218 deletions

View File

@@ -227,6 +227,9 @@ type
sfInnerProc, # proc is an inner proc sfInnerProc, # proc is an inner proc
sfThread, # proc will run as a thread sfThread, # proc will run as a thread
# variable is a thread variable # variable is a thread variable
sfInline # forced-inline procs
sfImmediate, # macro or template is immediately expanded without
# considering any possible overloads
sfCompileTime, # proc can be evaluated at compile time sfCompileTime, # proc can be evaluated at compile time
sfMerge, # proc can be merged with itself sfMerge, # proc can be merged with itself
sfDeadCodeElim, # dead code elimination for the module is turned on sfDeadCodeElim, # dead code elimination for the module is turned on
@@ -602,7 +605,8 @@ type
# the poor naming choices in the standard library. # the poor naming choices in the standard library.
const const
OverloadableSyms* = {skProc, skMethod, skIterator, skConverter, skModule} OverloadableSyms* = {skProc, skMethod, skIterator, skConverter,
skModule, skTemplate, skMacro}
GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody, GenericTypes*: TTypeKinds = {tyGenericInvokation, tyGenericBody,
tyGenericParam} tyGenericParam}

View File

@@ -567,6 +567,19 @@ proc StrTableRawInsert(data: var TSymSeq, n: PSym) =
assert(data[h] == nil) assert(data[h] == nil)
data[h] = n data[h] = n
proc SymTabReplaceRaw(data: var TSymSeq, prevSym: PSym, newSym: PSym) =
assert prevSym.name.h == newSym.name.h
var h: THash = prevSym.name.h and high(data)
while data[h] != nil:
if data[h] == prevSym:
data[h] = newSym
return
h = nextTry(h, high(data))
assert false
proc SymTabReplace*(t: var TStrTable, prevSym: PSym, newSym: PSym) =
SymTabReplaceRaw(t.data, prevSym, newSym)
proc StrTableEnlarge(t: var TStrTable) = proc StrTableEnlarge(t: var TStrTable) =
var n: TSymSeq var n: TSymSeq
newSeq(n, len(t.data) * growthFactor) newSeq(n, len(t.data) * growthFactor)

View File

@@ -10,7 +10,8 @@
proc leftAppearsOnRightSide(le, ri: PNode): bool = proc leftAppearsOnRightSide(le, ri: PNode): bool =
if le != nil: if le != nil:
for i in 1 .. <ri.len: for i in 1 .. <ri.len:
if le.isPartOf(ri[i]) != arNo: return true let r = ri[i]
if isPartOf(le, r) != arNo: return true
proc hasNoInit(call: PNode): bool {.inline.} = proc hasNoInit(call: PNode): bool {.inline.} =
result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags result = call.sons[0].kind == nkSym and sfNoInit in call.sons[0].sym.flags

View File

@@ -164,7 +164,7 @@ proc QualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
result = nil result = nil
if result != nil and result.kind == skStub: loadStub(result) if result != nil and result.kind == skStub: loadStub(result)
proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case n.kind case n.kind
of nkIdent, nkAccQuoted: of nkIdent, nkAccQuoted:
var ident = considerAcc(n) var ident = considerAcc(n)
@@ -174,7 +174,7 @@ proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
dec(o.stackPtr) dec(o.stackPtr)
if o.stackPtr < 0: break if o.stackPtr < 0: break
result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], ident) result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], ident)
of nkSym: of nkSym:
result = n.sym result = n.sym
o.mode = oimDone o.mode = oimDone
of nkDotExpr: of nkDotExpr:
@@ -204,6 +204,13 @@ proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
Incl(o.inSymChoice, result.id) Incl(o.inSymChoice, result.id)
else: nil else: nil
if result != nil and result.kind == skStub: loadStub(result) if result != nil and result.kind == skStub: loadStub(result)
proc lastOverloadScope*(o: TOverloadIter): int =
case o.mode
of oimNoQualifier: result = o.stackPtr
of oimSelfModule: result = ModuleTablePos
of oimOtherModule: result = ImportTablePos
else: result = -1
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case o.mode case o.mode

View File

@@ -26,8 +26,9 @@ const
wNoStackFrame, wError, wDiscardable, wNoInit} wNoStackFrame, wError, wDiscardable, wNoInit}
converterPragmas* = procPragmas converterPragmas* = procPragmas
methodPragmas* = procPragmas methodPragmas* = procPragmas
macroPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, templatePragmas* = {wImmediate}
wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern, macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideEffect, wCompilerProc, wDeprecated, wExtern,
wImportcpp, wImportobjc, wError, wDiscardable} wImportcpp, wImportobjc, wError, wDiscardable}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect, iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideEffect, wSideEffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
@@ -451,6 +452,9 @@ proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
of wImportCompilerProc: of wImportCompilerProc:
processImportCompilerProc(sym, getOptionalStr(c, it, sym.name.s)) processImportCompilerProc(sym, getOptionalStr(c, it, sym.name.s))
of wExtern: setExternName(sym, expectStrLit(c, it)) of wExtern: setExternName(sym, expectStrLit(c, it))
of wImmediate:
if sym.kind notin {skTemplate, skMacro}: invalidPragma(it)
incl(sym.flags, sfImmediate)
of wImportCpp: of wImportCpp:
processImportCpp(sym, getOptionalStr(c, it, sym.name.s)) processImportCpp(sym, getOptionalStr(c, it, sym.name.s))
of wImportObjC: of wImportObjC:

View File

@@ -10,11 +10,11 @@
# This module implements the semantic checking pass. # This module implements the semantic checking pass.
import import
strutils, hashes, lists, options, lexer, ast, astalgo, trees, treetab, ast, strutils, hashes, lists, options, lexer, astalgo, trees, treetab,
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
magicsys, parser, nversion, semdata, nimsets, semfold, importer, magicsys, parser, nversion, nimsets, semfold, importer,
procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch,
semthreads, intsets, transf, evals, idgen, aliases suggest, semthreads, intsets, transf, evals, idgen, aliases
proc semPass*(): TPass proc semPass*(): TPass
# implementation # implementation
@@ -75,17 +75,20 @@ proc semWhen(c: PContext, n: PNode, semCheck: bool = true): PNode
include semtempl include semtempl
proc semConstExpr(c: PContext, n: PNode): PNode = proc evalTypedExpr(c: PContext, e: PNode): PNode =
var e = semExprWithType(c, n)
if e == nil:
GlobalError(n.info, errConstExprExpected)
return nil
result = getConstExpr(c.module, e) result = getConstExpr(c.module, e)
if result == nil: if result == nil:
result = evalConstExpr(c.module, e) result = evalConstExpr(c.module, e)
if result == nil or result.kind == nkEmpty: if result == nil or result.kind == nkEmpty:
GlobalError(n.info, errConstExprExpected) GlobalError(e.info, errConstExprExpected)
proc semConstExpr(c: PContext, n: PNode): PNode =
var e = semExprWithType(c, n)
if e == nil:
GlobalError(n.info, errConstExprExpected)
return nil
result = evalTypedExpr(c, e)
proc semAndEvalConstExpr(c: PContext, n: PNode): PNode = proc semAndEvalConstExpr(c: PContext, n: PNode): PNode =
result = semConstExpr(c, n) result = semConstExpr(c, n)

View File

@@ -8,6 +8,7 @@
# #
## This module implements semantic checking for calls. ## This module implements semantic checking for calls.
# included from sem.nim
proc sameMethodDispatcher(a, b: PSym): bool = proc sameMethodDispatcher(a, b: PSym): bool =
result = false result = false
@@ -17,70 +18,78 @@ proc sameMethodDispatcher(a, b: PSym): bool =
if aa.kind == nkSym and bb.kind == nkSym and aa.sym == bb.sym: if aa.kind == nkSym and bb.kind == nkSym and aa.sym == bb.sym:
result = true result = true
proc semDirectCallWithBinding(c: PContext, n, f: PNode, filter: TSymKinds, proc resolveOverloads(c: PContext, n, orig: PNode,
initialBinding: PNode): PNode = filter: TSymKinds): TCandidate =
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:
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200)
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 initialBinding: PNode
var f = n.sons[0] var f = n.sons[0]
if f.kind == nkBracketExpr: if f.kind == nkBracketExpr:
# fill in the bindings: # fill in the bindings:
initialBinding = f initialBinding = f
f = f.sons[0] f = f.sons[0]
else: else:
initialBinding = nil initialBinding = nil
result = semDirectCallWithBinding(c, n, f, filter, initialBinding)
var
o: TOverloadIter
alt, z: TCandidate
template best: expr = result
#Message(n.info, warnUser, renderTree(n))
var sym = initOverloadIter(o, c, f)
var symScope = o.lastOverloadScope
if sym == nil: return
initCandidate(best, sym, initialBinding, symScope)
initCandidate(alt, sym, initialBinding, symScope)
while sym != nil:
if sym.kind in filter:
initCandidate(z, sym, initialBinding, o.lastOverloadScope)
z.calleeSym = sym
matches(c, n, orig, z)
if z.state == csMatch:
# little hack so that iterators are preferred over everything else:
if sym.kind == skIterator: inc(z.exactMatches, 200)
case best.state
of csEmpty, csNoMatch: best = z
of csMatch:
var cmp = cmpCandidates(best, z)
if cmp < 0: best = z # x is better than the best so far
elif cmp == 0: alt = z # x is as good as the best so far
else: nil
sym = nextOverloadIter(o, c, f)
if best.state == csEmpty:
# no overloaded proc found
# do not generate an error yet; the semantic checking will check for
# an overloaded () operator
elif alt.state == csMatch and cmpCandidates(best, alt) == 0 and
not sameMethodDispatcher(best.calleeSym, alt.calleeSym):
if best.state != csMatch:
InternalError(n.info, "x.state is not csMatch")
LocalError(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
getProcHeader(best.calleeSym), getProcHeader(alt.calleeSym),
best.calleeSym.Name.s])
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
assert x.state == csMatch
var finalCallee = x.calleeSym
markUsed(n, finalCallee)
if finalCallee.ast == nil:
internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
# a generic proc!
finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
result = x.call
result.sons[0] = newSymNode(finalCallee)
result.typ = finalCallee.typ.sons[0]
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
filter: TSymKinds): PNode =
var r = resolveOverloads(c, n, nOrig, filter)
if r.state == csMatch: result = semResolvedCall(c, n, r)
proc explicitGenericInstError(n: PNode): PNode = proc explicitGenericInstError(n: PNode): PNode =
LocalError(n.info, errCannotInstantiateX, renderTree(n)) LocalError(n.info, errCannotInstantiateX, renderTree(n))
result = n result = n

View File

@@ -73,7 +73,7 @@ type
userPragmas*: TStrTable userPragmas*: TStrTable
evalContext*: PEvalContext evalContext*: PEvalContext
slurpedFiles*: seq[string] slurpedFiles*: seq[string]
var var
gGenericsCache: PGenericsCache # save for modularity gGenericsCache: PGenericsCache # save for modularity

View File

@@ -8,6 +8,7 @@
# #
# this module does the semantic checking for expressions # this module does the semantic checking for expressions
# included from sem.nim
proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode = proc semTemplateExpr(c: PContext, n: PNode, s: PSym, semCheck = true): PNode =
markUsed(n, s) markUsed(n, s)
@@ -103,7 +104,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
if s.ast == nil: InternalError(n.info, "no default for") if s.ast == nil: InternalError(n.info, "no default for")
result = semExpr(c, s.ast) result = semExpr(c, s.ast)
of skType: of skType:
if efAllowType notin flags: GlobalError(n.info, errATypeHasNoValue) if efAllowType notin flags:
GlobalError(n.info, errATypeHasNoValue)
markUsed(n, s) markUsed(n, s)
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
else: else:
@@ -245,17 +247,17 @@ proc semIs(c: PContext, n: PNode): PNode =
else: else:
GlobalError(n.info, errXExpectsTwoArguments, "is") GlobalError(n.info, errXExpectsTwoArguments, "is")
proc semOpAux(c: PContext, n: PNode) = proc semOpAux(c: PContext, n: PNode) =
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
if a.kind == nkExprEqExpr and sonsLen(a) == 2: if a.kind == nkExprEqExpr and sonsLen(a) == 2:
var info = a.sons[0].info var info = a.sons[0].info
a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info) a.sons[0] = newIdentNode(considerAcc(a.sons[0]), info)
a.sons[1] = semExprWithType(c, a.sons[1]) a.sons[1] = semExprWithType(c, a.sons[1], {efAllowType})
a.typ = a.sons[1].typ a.typ = a.sons[1].typ
else: else:
n.sons[i] = semExprWithType(c, a) n.sons[i] = semExprWithType(c, a, {efAllowType})
proc overloadedCallOpr(c: PContext, n: PNode): PNode = proc overloadedCallOpr(c: PContext, n: PNode): PNode =
# quick check if there is *any* () operator overloaded: # quick check if there is *any* () operator overloaded:
var par = getIdent("()") var par = getIdent("()")
@@ -514,25 +516,32 @@ proc semStaticExpr(c: PContext, n: PNode): PNode =
if result.isNil: if result.isNil:
LocalError(n.info, errCannotInterpretNodeX, renderTree(n)) LocalError(n.info, errCannotInterpretNodeX, renderTree(n))
proc semDirectCallAnalyseEffects(c: PContext, n: PNode, proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode = flags: TExprFlags): PNode =
if efWantIterator in flags: if efWantIterator in flags:
result = semDirectCall(c, n, {skIterator}) result = semOverloadedCall(c, n, nOrig, {skIterator})
elif efInTypeOf in flags: elif efInTypeOf in flags:
# for ``type(countup(1,3))``, see ``tests/ttoseq``. # for ``type(countup(1,3))``, see ``tests/ttoseq``.
result = semDirectCall(c, n, {skIterator, skProc, skMethod, skConverter}) result = semOverloadedCall(c, n, nOrig, {skIterator, skProc, skMethod, skConverter, skMacro, skTemplate})
else: else:
result = semDirectCall(c, n, {skProc, skMethod, skConverter}) result = semOverloadedCall(c, n, nOrig, {skProc, skMethod, skConverter, skMacro, skTemplate})
if result != nil: if result != nil:
if result.sons[0].kind != nkSym: if result.sons[0].kind != nkSym:
InternalError("semDirectCallAnalyseEffects") InternalError("semDirectCallAnalyseEffects")
var callee = result.sons[0].sym let callee = result.sons[0].sym
if (callee.kind == skIterator) and (callee.id == c.p.owner.id): case callee.kind
GlobalError(n.info, errRecursiveDependencyX, callee.name.s) of skMacro, skTemplate: nil
if sfNoSideEffect notin callee.flags: else:
if {sfImportc, sfSideEffect} * callee.flags != {}: if (callee.kind == skIterator) and (callee.id == c.p.owner.id):
incl(c.p.owner.flags, sfSideEffect) GlobalError(n.info, errRecursiveDependencyX, callee.name.s)
if sfNoSideEffect notin callee.flags:
if {sfImportc, sfSideEffect} * callee.flags != {}:
incl(c.p.owner.flags, sfSideEffect)
proc semDirectCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
flags: TExprFlags): PNode =
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
result = nil result = nil
var prc = n.sons[0] var prc = n.sons[0]
@@ -548,13 +557,14 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
return semExpr(c, result, flags) return semExpr(c, result, flags)
else: else:
n.sons[0] = semExpr(c, n.sons[0]) n.sons[0] = semExpr(c, n.sons[0])
let nOrig = n.copyTree
semOpAux(c, n) semOpAux(c, n)
var t: PType = nil var t: PType = nil
if (n.sons[0].typ != nil): t = skipTypes(n.sons[0].typ, abstractInst) if (n.sons[0].typ != nil): t = skipTypes(n.sons[0].typ, abstractInst)
if (t != nil) and (t.kind == tyProc): if (t != nil) and (t.kind == tyProc):
var m: TCandidate var m: TCandidate
initCandidate(m, t) initCandidate(m, t)
matches(c, n, m) matches(c, n, nOrig, m)
if m.state != csMatch: if m.state != csMatch:
var msg = msgKindToString(errTypeMismatch) var msg = msgKindToString(errTypeMismatch)
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
@@ -575,7 +585,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# the old ``prc`` (which is likely an nkIdent) has to be restored: # the old ``prc`` (which is likely an nkIdent) has to be restored:
if result == nil: if result == nil:
n.sons[0] = prc n.sons[0] = prc
result = semDirectCallAnalyseEffects(c, n, flags) nOrig.sons[0] = prc
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil: if result == nil:
GlobalError(n.info, errExprXCannotBeCalled, GlobalError(n.info, errExprXCannotBeCalled,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))
@@ -587,15 +598,21 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
# this seems to be a hotspot in the compiler! # this seems to be a hotspot in the compiler!
let nOrig = n.copyTree
semOpAux(c, n) semOpAux(c, n)
result = semDirectCallAnalyseEffects(c, n, flags) result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil: if result == nil:
result = overloadedCallOpr(c, n) result = overloadedCallOpr(c, n)
if result == nil: GlobalError(n.Info, errGenerated, getNotFoundError(c, n)) if result == nil: GlobalError(n.Info, errGenerated, getNotFoundError(c, n))
fixAbstractType(c, result) let callee = result.sons[0].sym
analyseIfAddressTakenInCall(c, result) case callee.kind
if result.sons[0].sym.magic != mNone: of skMacro: result = semMacroExpr(c, nOrig, callee)
result = magicsAfterOverloadResolution(c, result, flags) of skTemplate: result = semTemplateExpr(c, nOrig, callee)
else:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
if callee.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags)
result = evalAtCompileTime(c, result) result = evalAtCompileTime(c, result)
proc buildStringify(c: PContext, arg: PNode): PNode = proc buildStringify(c: PContext, arg: PNode): PNode =
@@ -734,7 +751,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
result.typ = ty result.typ = ty
markUsed(n, f) markUsed(n, f)
return return
elif efAllowType notin flags: elif efAllowType notin flags:
GlobalError(n.sons[0].info, errATypeHasNoValue) GlobalError(n.sons[0].info, errATypeHasNoValue)
return return
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
@@ -852,16 +869,16 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# overloaded [] operator: # overloaded [] operator:
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]")) result = semExpr(c, buildOverloadedSubscripts(n, getIdent"[]"))
proc propertyWriteAccess(c: PContext, n, a: PNode): PNode = proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
var id = considerAcc(a[1]) var id = considerAcc(a[1])
result = newNodeI(nkCall, n.info) let setterId = newIdentNode(getIdent(id.s & '='), n.info)
addSon(result, newIdentNode(getIdent(id.s & '='), n.info))
# a[0] is already checked for semantics, that does ``builtinFieldAccess`` # a[0] is already checked for semantics, that does ``builtinFieldAccess``
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
# nodes? # nodes?
addSon(result, a[0]) let aOrig = nOrig[0]
addSon(result, semExpr(c, n[1])) result = newNode(nkCall, n.info, sons = @[setterId, a[0], semExpr(c, n[1])])
result = semDirectCallAnalyseEffects(c, result, {}) let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]])
result = semDirectCallAnalyseEffects(c, result, orig, {})
if result != nil: if result != nil:
fixAbstractType(c, result) fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result) analyseIfAddressTakenInCall(c, result)
@@ -898,9 +915,10 @@ proc semAsgn(c: PContext, n: PNode): PNode =
of nkDotExpr: of nkDotExpr:
# r.f = x # r.f = x
# --> `f=` (r, x) # --> `f=` (r, x)
let nOrig = n.copyTree
a = builtinFieldAccess(c, a, {efLValue}) a = builtinFieldAccess(c, a, {efLValue})
if a == nil: if a == nil:
return propertyWriteAccess(c, n, n[0]) return propertyWriteAccess(c, n, nOrig, a)
of nkBracketExpr: of nkBracketExpr:
# a[i] = x # a[i] = x
# --> `[]=`(a, i, x) # --> `[]=`(a, i, x)
@@ -1275,8 +1293,16 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
var s = qualifiedLookup(c, n.sons[0], {checkUndeclared}) var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
if s != nil: if s != nil:
case s.kind case s.kind
of skMacro: result = semMacroExpr(c, n, s) of skMacro:
of skTemplate: result = semTemplateExpr(c, n, s) if false and sfImmediate notin s.flags: # XXX not yet enabled
result = semDirectOp(c, n, flags)
else:
result = semMacroExpr(c, n, s)
of skTemplate:
if sfImmediate notin s.flags:
result = semDirectOp(c, n, flags)
else:
result = semTemplateExpr(c, n, s)
of skType: of skType:
# XXX think about this more (``set`` procs) # XXX think about this more (``set`` procs)
if n.len == 2: if n.len == 2:

View File

@@ -8,6 +8,7 @@
# #
## this module does the semantic checking of statements ## this module does the semantic checking of statements
# included from sem.nim
proc semCommand(c: PContext, n: PNode): PNode = proc semCommand(c: PContext, n: PNode): PNode =
result = semExprNoType(c, n) result = semExprNoType(c, n)
@@ -690,8 +691,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
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)
# XXX: obsoleted - happens in semParamList
# addParams(c, s.typ.n)
else: else:
s.typ = newTypeS(tyProc, c) s.typ = newTypeS(tyProc, c)
addSon(s.typ, nil) addSon(s.typ, nil)

View File

@@ -7,6 +7,8 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# included from sem.nim
proc isExpr(n: PNode): bool = proc isExpr(n: PNode): bool =
# returns true if ``n`` looks like an expression # returns true if ``n`` looks like an expression
case n.kind case n.kind
@@ -54,11 +56,6 @@ proc evalTemplateArgs(c: PContext, n: PNode, s: PSym): PNode =
else: arg = copyTree(s.typ.n.sons[i].sym.ast) else: arg = copyTree(s.typ.n.sons[i].sym.ast)
if arg == nil or arg.kind == nkEmpty: if arg == nil or arg.kind == nkEmpty:
LocalError(n.info, errWrongNumberOfArguments) LocalError(n.info, errWrongNumberOfArguments)
elif not (s.typ.sons[i].kind in {tyTypeDesc, tyStmt, tyExpr}):
# concrete type means semantic checking for argument:
# XXX This is horrible! Better make semantic checking use some kind
# of fixpoint iteration ...
arg = fitNode(c, s.typ.sons[i], semExprWithType(c, arg))
addSon(result, arg) addSon(result, arg)
proc evalTemplate*(c: PContext, n: PNode, sym: PSym): PNode = proc evalTemplate*(c: PContext, n: PNode, sym: PSym): PNode =
@@ -167,9 +164,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
# check parameter list: # check parameter list:
pushOwner(s) pushOwner(s)
openScope(c.tab) openScope(c.tab)
n.sons[namePos] = newSymNode(s) # check that no pragmas exist: n.sons[namePos] = newSymNode(s)
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
LocalError(n.info, errNoPragmasAllowedForX, "template") pragma(c, s, n.sons[pragmasPos], templatePragmas)
# check that no generic parameters exist: # check that no generic parameters exist:
if n.sons[genericParamsPos].kind != nkEmpty: if n.sons[genericParamsPos].kind != nkEmpty:
LocalError(n.info, errNoGenericParamsAllowedForX, "template") LocalError(n.info, errNoGenericParamsAllowedForX, "template")
@@ -185,8 +182,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
# use ``stmt`` as implicit result type # use ``stmt`` as implicit result type
s.typ.sons[0] = newTypeS(tyStmt, c) s.typ.sons[0] = newTypeS(tyStmt, c)
s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0]) s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0])
# XXX: obsoleted - happens in semParamList #
# addParams(c, s.typ.n) # resolve parameters:
var toBind = initIntSet() var toBind = initIntSet()
n.sons[bodyPos] = resolveTemplateParams(c, n.sons[bodyPos], false, toBind) n.sons[bodyPos] = resolveTemplateParams(c, n.sons[bodyPos], false, toBind)
if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}: if s.typ.sons[0].kind notin {tyStmt, tyTypeDesc}:
@@ -198,5 +193,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
result = n result = n
if n.sons[bodyPos].kind == nkEmpty: if n.sons[bodyPos].kind == nkEmpty:
LocalError(n.info, errImplOfXexpected, s.name.s) LocalError(n.info, errImplOfXexpected, s.name.s)
# add identifier of template as a last step to not allow recursive templates: let curScope = c.tab.tos - 1
addInterfaceDecl(c, s) var proto = SearchForProc(c, s, curScope)
if proto == nil:
addInterfaceOverloadableSymAt(c, s, curScope)
else:
SymTabReplace(c.tab.stack[curScope], proto, s)

View File

@@ -8,6 +8,7 @@
# #
# this module does the semantic checking of type declarations # this module does the semantic checking of type declarations
# included from sem.nim
proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if prev == nil: if prev == nil:

View File

@@ -17,6 +17,7 @@ import
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
@@ -26,6 +27,7 @@ type
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
calleeScope: int # may be -1 for unknown scope
call*: PNode # modified call call*: PNode # modified call
bindings*: TIdTable # maps types to types bindings*: TIdTable # maps types to types
baseTypeMatch: bool # needed for conversions from T to openarray[T] baseTypeMatch: bool # needed for conversions from T to openarray[T]
@@ -35,7 +37,7 @@ type
isNone, isConvertible, isIntConv, isSubtype, isNone, isConvertible, isIntConv, isSubtype,
isGeneric, isGeneric,
isEqual isEqual
proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} = proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
c.exactMatches = 0 c.exactMatches = 0
c.subtypeMatches = 0 c.subtypeMatches = 0
@@ -59,9 +61,10 @@ proc put(t: var TIdTable, key, val: PType) {.inline.} =
IdentEq(val.sym.name, "TTable"): IdentEq(val.sym.name, "TTable"):
assert false assert false
proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode) = proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode, calleeScope = -1) =
initCandidateAux(c, callee.typ) initCandidateAux(c, callee.typ)
c.calleeSym = callee c.calleeSym = callee
c.calleeScope = calleeScope
initIdTable(c.bindings) initIdTable(c.bindings)
if binding != nil: if binding != nil:
var typeParams = callee.ast[genericParamsPos] var typeParams = callee.ast[genericParamsPos]
@@ -93,6 +96,9 @@ proc cmpCandidates*(a, b: TCandidate): int =
result = a.intConvMatches - b.intConvMatches result = a.intConvMatches - b.intConvMatches
if result != 0: return if result != 0: return
result = a.convMatches - b.convMatches result = a.convMatches - b.convMatches
if result != 0: return
if (a.calleeScope != -1) and (b.calleeScope != -1):
result = a.calleeScope - b.calleeScope
proc writeMatches(c: TCandidate) = proc writeMatches(c: TCandidate) =
Writeln(stdout, "exact matches: " & $c.exactMatches) Writeln(stdout, "exact matches: " & $c.exactMatches)
@@ -101,10 +107,10 @@ 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 semOverloadedCall,
# as semDirectCall is already pretty slow (and we need this information only # as semOverlodedCall is already pretty slow (and we need this information
# in case of an error). # only in case of an error).
result = msgKindToString(errTypeMismatch) result = msgKindToString(errTypeMismatch)
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
#debug(n.sons[i].typ) #debug(n.sons[i].typ)
@@ -507,7 +513,10 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
return return
proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
arg: 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:
@@ -547,9 +556,9 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
result = userConvMatch(c, m, base(f), a, arg) result = userConvMatch(c, m, base(f), a, arg)
proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType, proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
arg: PNode): PNode = arg, argOrig: PNode): PNode =
if arg == nil or arg.kind != nkSymChoice: if arg == nil or arg.kind != nkSymChoice:
result = ParamTypesMatchAux(c, m, f, a, arg) result = ParamTypesMatchAux(c, m, f, a, arg, argOrig)
else: else:
# CAUTION: The order depends on the used hashing scheme. Thus it is # CAUTION: The order depends on the used hashing scheme. Thus it is
# incorrect to simply use the first fitting match. However, to implement # incorrect to simply use the first fitting match. However, to implement
@@ -591,29 +600,29 @@ proc ParamTypesMatch(c: PContext, m: var TCandidate, f, a: PType,
else: else:
# only one valid interpretation found: # only one valid interpretation found:
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], argOrig)
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, nil)
proc ConvertTo*(c: PContext, f: PType, n: PNode): PNode = proc ConvertTo*(c: PContext, f: PType, n: PNode): PNode =
var m: TCandidate var m: TCandidate
initCandidate(m, f) initCandidate(m, f)
result = paramTypesMatch(c, m, f, n.typ, n) result = paramTypesMatch(c, m, f, n.typ, n, nil)
proc argtypeMatches*(c: PContext, f, a: PType): bool = proc argtypeMatches*(c: PContext, f, a: PType): bool =
var m: TCandidate var m: TCandidate
initCandidate(m, f) initCandidate(m, f)
result = paramTypesMatch(c, m, f, a, ast.emptyNode) != nil result = paramTypesMatch(c, m, f, a, ast.emptyNode, nil) != nil
proc setSon(father: PNode, at: int, son: PNode) = 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 matchesAux*(c: PContext, n: PNode, m: var TCandidate, proc matchesAux*(c: PContext, n, nOrig: PNode,
marker: var TIntSet) = m: var TCandidate, marker: var TIntSet) =
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
@@ -623,7 +632,7 @@ proc matchesAux*(c: PContext, n: PNode, m: var TCandidate,
addSon(m.call, copyTree(n.sons[0])) addSon(m.call, copyTree(n.sons[0]))
var container: PNode = nil # constructed container var container: PNode = nil # constructed container
var formal: PSym = nil var formal: PSym = nil
while a < sonsLen(n): while a < n.len:
if n.sons[a].kind == nkExprEqExpr: if n.sons[a].kind == nkExprEqExpr:
# named param # named param
# check if m.callee has such a param: # check if m.callee has such a param:
@@ -642,8 +651,8 @@ proc matchesAux*(c: PContext, n: PNode, m: var TCandidate,
m.state = csNoMatch m.state = csNoMatch
return return
m.baseTypeMatch = false m.baseTypeMatch = false
var arg = ParamTypesMatch(c, m, formal.typ, var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ,
n.sons[a].typ, n.sons[a].sons[1]) n.sons[a].sons[1], nOrig.sons[a].sons[1])
if arg == nil: if arg == nil:
m.state = csNoMatch m.state = csNoMatch
return return
@@ -666,9 +675,10 @@ proc matchesAux*(c: PContext, n: PNode, m: var TCandidate,
copyTree(n.sons[a]), m, c)) copyTree(n.sons[a]), m, c))
else: else:
addSon(m.call, copyTree(n.sons[a])) addSon(m.call, copyTree(n.sons[a]))
elif formal != nil: elif formal != nil:
m.baseTypeMatch = false m.baseTypeMatch = false
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a]) var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a])
if (arg != nil) and m.baseTypeMatch and (container != nil): if (arg != nil) and m.baseTypeMatch and (container != nil):
addSon(container, arg) addSon(container, arg)
else: else:
@@ -687,7 +697,8 @@ proc matchesAux*(c: PContext, n: PNode, m: var TCandidate,
m.state = csNoMatch m.state = csNoMatch
return return
m.baseTypeMatch = false m.baseTypeMatch = false
var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ, n.sons[a]) var arg = ParamTypesMatch(c, m, formal.typ, n.sons[a].typ,
n.sons[a], nOrig.sons[a])
if arg == nil: if arg == nil:
m.state = csNoMatch m.state = csNoMatch
return return
@@ -703,14 +714,14 @@ proc matchesAux*(c: PContext, n: PNode, m: var TCandidate,
inc(a) inc(a)
inc(f) inc(f)
proc partialMatch*(c: PContext, n: PNode, m: var TCandidate) = proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
# for 'suggest' support: # for 'suggest' support:
var marker = initIntSet() var marker = initIntSet()
matchesAux(c, n, m, marker) matchesAux(c, n, nOrig, m, marker)
proc matches*(c: PContext, n: PNode, m: var TCandidate) = proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
var marker = initIntSet() var marker = initIntSet()
matchesAux(c, n, m, marker) matchesAux(c, n, nOrig, m, marker)
if m.state == csNoMatch: return if m.state == csNoMatch: return
# check that every formal parameter got a value: # check that every formal parameter got a value:
var f = 1 var f = 1

View File

@@ -46,7 +46,7 @@ proc suggestField(s: PSym) =
if filterSym(s): if filterSym(s):
OutWriteln(SymToStr(s, isLocal=true, sectionSuggest)) OutWriteln(SymToStr(s, isLocal=true, sectionSuggest))
template wholeSymTab(cond, section: expr) = template wholeSymTab(cond, section: expr) {.immediate.} =
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 cond: if cond:
@@ -79,20 +79,18 @@ proc nameFits(c: PContext, s: PSym, n: PNode): bool =
else: return false else: return false
result = opr.id == s.name.id result = opr.id == s.name.id
proc argsFit(c: PContext, candidate: PSym, n: PNode): bool = proc argsFit(c: PContext, candidate: PSym, n, nOrig: PNode): bool =
case candidate.kind case candidate.kind
of skProc, skIterator, skMethod: of OverloadableSyms:
var m: TCandidate var m: TCandidate
initCandidate(m, candidate, nil) initCandidate(m, candidate, nil)
sigmatch.partialMatch(c, n, m) sigmatch.partialMatch(c, n, nOrig, m)
result = m.state != csNoMatch result = m.state != csNoMatch
of skTemplate, skMacro:
result = true
else: else:
result = false result = false
proc suggestCall(c: PContext, n: PNode) = proc suggestCall(c: PContext, n, nOrig: PNode) =
wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n), wholeSymTab(filterSym(it) and nameFits(c, it, n) and argsFit(c, it, n, nOrig),
sectionContext) sectionContext)
proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} = proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
@@ -227,7 +225,7 @@ proc suggestExpr*(c: PContext, node: PNode) =
var x = safeSemExpr(c, n.sons[i]) var x = safeSemExpr(c, n.sons[i])
if x.kind == nkEmpty or x.typ == nil: break if x.kind == nkEmpty or x.typ == nil: break
addSon(a, x) addSon(a, x)
suggestCall(c, a) suggestCall(c, a, n)
if optDef in gGlobalOptions: if optDef in gGlobalOptions:
var n = findClosestSym(fuzzySemCheck(c, node)) var n = findClosestSym(fuzzySemCheck(c, node))

View File

@@ -36,7 +36,7 @@ type
wColon, wColonColon, wEquals, wDot, wDotDot, wColon, wColonColon, wEquals, wDot, wDotDot,
wStar, wMinus, wStar, wMinus,
wMagic, wThread, wFinal, wProfiler, wObjChecks, wMagic, wThread, wFinal, wProfiler, wObjChecks,
wImportCpp, wImportObjC, wImmediate, wImportCpp, wImportObjC,
wImportCompilerProc, wImportCompilerProc,
wImportc, wExportc, wExtern, wIncompleteStruct, wImportc, wExportc, wExtern, wIncompleteStruct,
wAlign, wNodecl, wPure, wVolatile, wRegister, wSideeffect, wHeader, wAlign, wNodecl, wPure, wVolatile, wRegister, wSideeffect, wHeader,
@@ -85,7 +85,7 @@ const
"*", "-", "*", "-",
"magic", "thread", "final", "profiler", "objchecks", "magic", "thread", "final", "profiler", "objchecks",
"importcpp", "importobjc", "immediate", "importcpp", "importobjc",
"importcompilerproc", "importc", "exportc", "extern", "incompletestruct", "importcompilerproc", "importc", "exportc", "extern", "incompletestruct",
"align", "nodecl", "pure", "volatile", "register", "sideeffect", "align", "nodecl", "pure", "volatile", "register", "sideeffect",
"header", "nosideeffect", "noreturn", "merge", "lib", "dynlib", "header", "nosideeffect", "noreturn", "merge", "lib", "dynlib",

View File

@@ -1,6 +1,6 @@
# All and any # All and any
template all(container, cond: expr): expr = template all(container, cond: expr): expr {.immediate.} =
block: block:
var result = true var result = true
for it in items(container): for it in items(container):
@@ -9,7 +9,7 @@ template all(container, cond: expr): expr =
break break
result result
template any(container, cond: expr): expr = template any(container, cond: expr): expr {.immediate.} =
block: block:
var result = false var result = false
for it in items(container): for it in items(container):

View File

@@ -50,7 +50,7 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool): seq[T] =
## Returns all items in a sequence that fulfilled the predicate. ## Returns all items in a sequence that fulfilled the predicate.
accumulateResult(filter(seq1, pred)) accumulateResult(filter(seq1, pred))
template filterIt*(seq1, pred: expr): expr = template filterIt*(seq1, pred: expr): expr {.immediate.} =
## Finds a specific item in a sequence as long as the ## Finds a specific item in a sequence as long as the
## predicate returns true. The predicate needs to be an expression ## predicate returns true. The predicate needs to be an expression
## containing ``it``: ``filterIt("abcxyz", it == 'x')``. ## containing ``it``: ``filterIt("abcxyz", it == 'x')``.

View File

@@ -17,15 +17,15 @@ type
proc `==` *(a, b: TColor): bool {.borrow.} proc `==` *(a, b: TColor): bool {.borrow.}
## compares two colors. ## compares two colors.
template extract(a: TColor, r, g, b: expr) = template extract(a: TColor, r, g, b: expr) {.immediate.}=
var r = a.int shr 16 and 0xff var r = a.int shr 16 and 0xff
var g = a.int shr 8 and 0xff var g = a.int shr 8 and 0xff
var b = a.int and 0xff var b = a.int and 0xff
template rawRGB(r, g, b: expr): expr = template rawRGB(r, g, b: int): expr =
TColor(r shl 16 or g shl 8 or b) TColor(r shl 16 or g shl 8 or b)
template colorOp(op: expr) = template colorOp(op: expr) {.immediate.} =
extract(a, ar, ag, ab) extract(a, ar, ag, ab)
extract(b, br, bg, bb) extract(b, br, bg, bb)
result = rawRGB(op(ar, br), op(ag, bg), op(ab, bb)) result = rawRGB(op(ar, br), op(ag, bg), op(ab, bb))

View File

@@ -242,12 +242,12 @@ proc UnixToNativePath*(path: string): string {.
inc(i) inc(i)
when defined(windows): when defined(windows):
template wrapUnary(varname, winApiProc, arg: expr) = template wrapUnary(varname, winApiProc, arg: expr) {.immediate.} =
var tmp = allocWideCString(arg) var tmp = allocWideCString(arg)
var varname = winApiProc(tmp) var varname = winApiProc(tmp)
dealloc tmp dealloc tmp
template wrapBinary(varname, winApiProc, arg, arg2: expr) = template wrapBinary(varname, winApiProc, arg, arg2: expr) {.immediate.} =
var tmp2 = allocWideCString(arg) var tmp2 = allocWideCString(arg)
var varname = winApiProc(tmp2, arg2) var varname = winApiProc(tmp2, arg2)
dealloc tmp2 dealloc tmp2

View File

@@ -624,15 +624,15 @@ proc `<` *[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.}
proc `<` *[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.} proc `<` *[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.}
proc `<` *(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.} proc `<` *(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.}
template `!=` * (x, y: expr): expr = template `!=` * (x, y: expr): expr {.immediate.} =
## unequals operator. This is a shorthand for ``not (x == y)``. ## unequals operator. This is a shorthand for ``not (x == y)``.
not (x == y) not (x == y)
template `>=` * (x, y: expr): expr = template `>=` * (x, y: expr): expr {.immediate.} =
## "is greater or equals" operator. This is the same as ``y <= x``. ## "is greater or equals" operator. This is the same as ``y <= x``.
y <= x y <= x
template `>` * (x, y: expr): expr = template `>` * (x, y: expr): expr {.immediate.} =
## "is greater" operator. This is the same as ``y < x``. ## "is greater" operator. This is the same as ``y < x``.
y < x y < x
@@ -655,11 +655,11 @@ proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.}
## is achieved by reversing the parameters for ``contains``; ``in`` then ## is achieved by reversing the parameters for ``contains``; ``in`` then
## passes its arguments in reverse order. ## passes its arguments in reverse order.
template `in` * (x, y: expr): expr = contains(y, x) template `in` * (x, y: expr): expr {.immediate.} = contains(y, x)
template `not_in` * (x, y: expr): expr = not contains(y, x) template `not_in` * (x, y: expr): expr {.immediate.} = not contains(y, x)
proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.} proc `is` *[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
template `is_not` *(x, y: expr): expr = not (x is y) template `is_not` *(x, y: expr): expr {.immediate.} = not (x is y)
proc `of` *[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.} proc `of` *[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.}
@@ -842,7 +842,7 @@ proc quit*(errorcode: int = QuitSuccess) {.
## It does *not* call the garbage collector to free all the memory, ## It does *not* call the garbage collector to free all the memory,
## unless a quit procedure calls ``GC_collect``. ## unless a quit procedure calls ``GC_collect``.
template sysAssert(cond, msg: expr) = template sysAssert(cond: bool, msg: string) =
when defined(useSysAssert): when defined(useSysAssert):
if not cond: if not cond:
echo "[SYSASSERT] ", msg echo "[SYSASSERT] ", msg
@@ -1090,11 +1090,11 @@ proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
## swaps the values `a` and `b`. This is often more efficient than ## swaps the values `a` and `b`. This is often more efficient than
## ``tmp = a; a = b; b = tmp``. Particularly useful for sorting algorithms. ## ``tmp = a; a = b; b = tmp``. Particularly useful for sorting algorithms.
template `>=%` *(x, y: expr): expr = y <=% x template `>=%` *(x, y: expr): expr {.immediate.} = y <=% x
## treats `x` and `y` as unsigned and compares them. ## treats `x` and `y` as unsigned and compares them.
## Returns true iff ``unsigned(x) >= unsigned(y)``. ## Returns true iff ``unsigned(x) >= unsigned(y)``.
template `>%` *(x, y: expr): expr = y <% x template `>%` *(x, y: expr): expr {.immediate.} = y <% x
## treats `x` and `y` as unsigned and compares them. ## treats `x` and `y` as unsigned and compares them.
## Returns true iff ``unsigned(x) > unsigned(y)``. ## Returns true iff ``unsigned(x) > unsigned(y)``.
@@ -1590,7 +1590,7 @@ proc echo*[Ty](x: openarray[Ty]) {.magic: "Echo", noSideEffect.}
## Unlike other IO operations this is guaranteed to be thread-safe as ## Unlike other IO operations this is guaranteed to be thread-safe as
## ``echo`` is very often used for debugging convenience. ## ``echo`` is very often used for debugging convenience.
template newException*(exceptn, message: expr): expr = template newException*(exceptn: typeDesc, message: string): expr =
## creates an exception object of type ``exceptn`` and sets its ``msg`` field ## creates an exception object of type ``exceptn`` and sets its ``msg`` field
## to `message`. Returns the new exception object. ## to `message`. Returns the new exception object.
block: # open a new scope block: # open a new scope
@@ -2033,7 +2033,7 @@ proc `[]`*(s: string, x: TSlice[int]): string {.inline.} =
## slice operation for strings. Negative indexes are supported. ## slice operation for strings. Negative indexes are supported.
result = s.substr(x.a-|s, x.b-|s) result = s.substr(x.a-|s, x.b-|s)
template spliceImpl(s, a, L, b: expr): stmt = template spliceImpl(s, a, L, b: expr): stmt {.immediate.} =
# make room for additional elements or cut: # make room for additional elements or cut:
var slen = s.len var slen = s.len
var shift = b.len - L var shift = b.len - L
@@ -2176,7 +2176,7 @@ proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
proc raiseAssert(msg: string) {.noinline.} = proc raiseAssert(msg: string) {.noinline.} =
raise newException(EAssertionFailed, msg) raise newException(EAssertionFailed, msg)
template assert*(cond: expr, msg = "") = template assert*(cond: bool, msg = "") =
## provides a means to implement `programming by contracts`:idx: in Nimrod. ## provides a means to implement `programming by contracts`:idx: in Nimrod.
## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it ## ``assert`` evaluates expression ``cond`` and if ``cond`` is false, it
## raises an ``EAssertionFailure`` exception. However, the compiler may ## raises an ``EAssertionFailure`` exception. However, the compiler may
@@ -2188,7 +2188,7 @@ template assert*(cond: expr, msg = "") =
if not cond: if not cond:
raiseAssert(astToStr(cond) & ' ' & msg) raiseAssert(astToStr(cond) & ' ' & msg)
template doAssert*(cond: expr, msg = "") = template doAssert*(cond: bool, msg = "") =
## same as `assert` but is always turned on and not affected by the ## same as `assert` but is always turned on and not affected by the
## ``--assertions`` command line switch. ## ``--assertions`` command line switch.
bind raiseAssert, InstantiationInfo bind raiseAssert, InstantiationInfo

View File

@@ -251,7 +251,7 @@ when defined(endb):
dbgAborting: bool # whether the debugger wants to abort dbgAborting: bool # whether the debugger wants to abort
proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} =
template processSignal(s, action: expr) = template processSignal(s, action: expr) {.immediate.} =
if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
elif s == SIGSEGV: elif s == SIGSEGV:
action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")

View File

@@ -170,7 +170,7 @@ when traceGC:
cfprintf(cstdout, "Allocations: %ld; ZCT freed: %ld; CYC freed: %ld\n", cfprintf(cstdout, "Allocations: %ld; ZCT freed: %ld; CYC freed: %ld\n",
e, z, y) e, z, y)
template gcTrace(cell, state: expr): stmt = template gcTrace(cell, state: expr): stmt {.immediate.} =
when traceGC: traceCell(cell, state) when traceGC: traceCell(cell, state)
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------

View File

@@ -1284,9 +1284,9 @@ type # This is the system-independent thread info struc
TProcedure* = proc () TProcedure* = proc ()
type TEventSeq = set[TEventKind] type TEventSeq = set[TEventKind]
template evconv(procName: expr, ptrName: typeDesc, assertions: TEventSeq): stmt = template evconv(procName: expr, ptrName: typeDesc, assertions: TEventSeq): stmt {.immediate.} =
proc `procName`*(event: PEvent): ptrName = proc `procName`*(event: PEvent): ptrName =
assert(assertions.contains(event.kind)) assert(contains(assertions, event.kind))
result = cast[ptrName](event) result = cast[ptrName](event)
evconv(EvActive, PActiveEvent, {ACTIVEEVENT}) evconv(EvActive, PActiveEvent, {ACTIVEEVENT})

View File

@@ -1,8 +1,6 @@
from sdl import PSurface from sdl import PSurface
discard SDL.CreateRGBSurface(SDL.SWSURFACE, 23, 34, discard SDL.CreateRGBSurface(SDL.SWSURFACE, 23, 34,
32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xff000000'i32) 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xff000000'i32)

29
tests/compile/tredef.nim Normal file
View File

@@ -0,0 +1,29 @@
template foo(a: int, b: string) = nil
foo(1, "test")
proc bar(a: int, b: string) = nil
bar(1, "test")
template foo(a: int, b: string) = bar(a, b)
foo(1, "test")
block:
proc bar(a: int, b: string) = nil
template foo(a: int, b: string) = nil
foo(1, "test")
bar(1, "test")
proc baz =
proc foo(a: int, b: string) = nil
proc foo(b: string) =
template bar(a: int, b: string) = nil
bar(1, "test")
foo("test")
block:
proc foo(b: string) = nil
foo("test")
foo(1, "test")
baz()

View File

@@ -1,6 +1,6 @@
# Test if the new table constructor syntax works: # Test if the new table constructor syntax works:
template ignoreExpr(e: expr): stmt = template ignoreExpr(e: expr): stmt {.immediate.} =
nil nil
# test first class '..' syntactical citizen: # test first class '..' syntactical citizen:

View File

@@ -26,13 +26,13 @@ echo(ha)
# Test identifier generation: # Test identifier generation:
template prefix(name: expr): expr = `"hu" name` template prefix(name: expr): expr {.immediate.} = `"hu" name`
var `hu "XYZ"` = "yay" var `hu "XYZ"` = "yay"
echo prefix(XYZ) echo prefix(XYZ)
template typedef(name: expr, typ: typeDesc) = template typedef(name: expr, typ: typeDesc) {.immediate.} =
type type
`T name`* = typ `T name`* = typ
`P name`* = ref `T name` `P name`* = ref `T name`

View File

@@ -1,7 +1,7 @@
template `:=`(name, val: expr): stmt = template `:=`(name, val: expr): stmt {.immediate.} =
var name = val var name = val
ha := 1 * 4 ha := 1 * 4
hu := "ta-da" == "ta-da" hu := "ta-da" == "ta-da"
echo ha, hu echo ha, hu

View File

@@ -0,0 +1,9 @@
discard """
file: "tprocredef.nim"
line: 8
errormsg: "redefinition of \'foo\'"
"""
proc foo(a: int, b: string) = nil
proc foo(a: int, b: string) = nil

View File

@@ -3,10 +3,10 @@ discard """
line: 18 line: 18
errormsg: "undeclared identifier: \'b\'" errormsg: "undeclared identifier: \'b\'"
""" """
template declareInScope(x: expr, t: typeDesc): stmt = template declareInScope(x: expr, t: typeDesc): stmt {.immediate.} =
var x: t var x: t
template declareInNewScope(x: expr, t: typeDesc): stmt = template declareInNewScope(x: expr, t: typeDesc): stmt {.immediate.} =
# open a new scope: # open a new scope:
block: block:
var x: t var x: t
@@ -17,5 +17,3 @@ a = 42 # works, `a` is known here
declareInNewScope(b, int) declareInNewScope(b, int)
b = 42 #ERROR_MSG undeclared identifier: 'b' b = 42 #ERROR_MSG undeclared identifier: 'b'

View File

@@ -1,4 +1,5 @@
discard """ discard """
disabled: true
output: '''derived class output: '''derived class
base class base class
''' '''

View File

@@ -1,5 +1,6 @@
discard """ discard """
output: '''derived class 2 disabled: true
output: '''derived class 2
base class base class
''' '''
""" """

View File

@@ -2,12 +2,12 @@ discard """
file: "tambsys.nim" file: "tambsys.nim"
output: "" output: ""
""" """
# Test ambiguous symbols # Test ambiguous symbols
import mambsys1, mambsys2 import mambsys1, mambsys2
var var
v: mambsys1.TExport v: mambsys1.TExport
mambsys2.foo(3) #OUT mambsys2.foo(3) #OUT

View File

@@ -21,7 +21,7 @@ template Comparable(typ: typeDesc): stmt =
proc `<=` * (x, y: typ): bool {.borrow.} proc `<=` * (x, y: typ): bool {.borrow.}
proc `==` * (x, y: typ): bool {.borrow.} proc `==` * (x, y: typ): bool {.borrow.}
template DefineCurrency(typ, base: expr): stmt = template DefineCurrency(typ, base: expr): stmt {.immediate.} =
type type
typ* = distinct base typ* = distinct base
Additive(typ) Additive(typ)

View File

@@ -6,7 +6,7 @@ discard """
var testNumber = 0 var testNumber = 0
template test(opr, a, b, c: expr): stmt = template test(opr, a, b, c: expr): stmt {.immediate.} =
# test the expression at compile and runtime # test the expression at compile and runtime
block: block:
const constExpr = opr(a, b) const constExpr = opr(a, b)
@@ -43,5 +43,3 @@ test(`shl`, 0xffffffff'i32, 0x4'i32, 0xfffffff0'i32)
Echo("Success") #OUT Success Echo("Success") #OUT Success

View File

@@ -1,6 +1,6 @@
discard """ discard """
file: "toverl2.nim" file: "toverl2.nim"
output: "true012" output: "true012innertrue"
""" """
# Test new overloading resolution rules # Test new overloading resolution rules
@@ -14,14 +14,20 @@ iterator toverl2(x: int): int =
while res < x: while res < x:
yield res yield res
inc(res) inc(res)
var var
pp: proc (x: bool): string = toverl2 pp: proc (x: bool): string = toverl2
stdout.write(pp(true)) stdout.write(pp(true))
for x in toverl2(3): for x in toverl2(3):
stdout.write(toverl2(x)) stdout.write(toverl2(x))
block:
proc toverl2(x: int): string = return "inner"
stdout.write(toverl2(5))
stdout.write(true)
stdout.write("\n") stdout.write("\n")
#OUT true012 #OUT true012innertrue

View File

@@ -2,7 +2,7 @@ discard """
output: "23456" output: "23456"
""" """
template toSeq*(iter: expr): expr = template toSeq*(iter: expr): expr {.immediate.} =
var result: seq[type(iter)] = @[] var result: seq[type(iter)] = @[]
for x in iter: add(result, x) for x in iter: add(result, x)
result result

View File

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

32
tests/run/utemplates.nim Normal file
View File

@@ -0,0 +1,32 @@
import unittest
template t(a: int): expr = "int"
template t(a: string): expr = "string"
test "templates can be overloaded":
check t(10) == "int"
check t("test") == "string"
test "previous definitions can be further overloaded or hidden in local scopes":
template t(a: bool): expr = "bool"
check t(true) == "bool"
check t(10) == "int"
template t(a: int): expr = "inner int"
check t(10) == "inner int"
check t("test") == "string"
test "templates can be redefined multiple times":
template customAssert(cond: bool, msg: string): stmt =
if not cond: fail(msg)
template assertion_failed(body: stmt) =
template fail(msg: string): stmt = body
assertion_failed: check msg == "first fail path"
customAssert false, "first fail path"
assertion_failed: check msg == "second fail path"
customAssert false, "second fail path"