Merge branch 'devel' of https://github.com/Araq/Nimrod into devel

This commit is contained in:
Araq 2014-03-10 22:23:38 +01:00
commit 3270676e77
34 changed files with 567 additions and 167 deletions

View file

@ -818,6 +818,9 @@ type
counter*: int counter*: int
data*: TObjectSeq data*: TObjectSeq
TImplication* = enum
impUnknown, impNo, impYes
# BUGFIX: a module is overloadable so that a proc can have the # BUGFIX: a module is overloadable so that a proc can have the
# same name as an imported module. This is necessary because of # same name as an imported module. This is necessary because of
# the poor naming choices in the standard library. # the poor naming choices in the standard library.
@ -865,6 +868,7 @@ const
nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix, nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
nkCommand, nkCallStrLit, nkHiddenCallConv} nkCommand, nkCallStrLit, nkHiddenCallConv}
nkLiterals* = {nkCharLit..nkTripleStrLit}
nkLambdaKinds* = {nkLambda, nkDo} nkLambdaKinds* = {nkLambda, nkDo}
declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef} declarativeDefs* = {nkProcDef, nkMethodDef, nkIteratorDef, nkConverterDef}
procDefs* = nkLambdaKinds + declarativeDefs procDefs* = nkLambdaKinds + declarativeDefs

View file

@ -157,6 +157,12 @@ proc leValue*(a, b: PNode): bool =
#InternalError(a.info, "leValue") #InternalError(a.info, "leValue")
discard discard
proc weakLeValue*(a, b: PNode): TImplication =
if a.kind notin nkLiterals or b.kind notin nkLiterals:
result = impUnknown
else:
result = if leValue(a, b): impYes else: impNo
proc lookupInRecord(n: PNode, field: PIdent): PSym = proc lookupInRecord(n: PNode, field: PIdent): PSym =
result = nil result = nil
case n.kind case n.kind

View file

@ -274,10 +274,6 @@ proc pred(n: PNode): PNode =
else: else:
result = n result = n
type
TImplication* = enum
impUnknown, impNo, impYes
proc impliesEq(fact, eq: PNode): TImplication = proc impliesEq(fact, eq: PNode): TImplication =
let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1) let (loc, val) = if isLocation(eq.sons[1]): (1, 2) else: (2, 1)

View file

@ -96,7 +96,7 @@ type
errOnlyACallOpCanBeDelegator, errUsingNoSymbol, errOnlyACallOpCanBeDelegator, errUsingNoSymbol,
errMacroBodyDependsOnGenericTypes, errMacroBodyDependsOnGenericTypes,
errDestructorNotGenericEnough, errDestructorNotGenericEnough,
errInlineIteratorsAsProcParams,
errXExpectsTwoArguments, errXExpectsTwoArguments,
errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations,
errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX,
@ -331,6 +331,8 @@ const
"because the parameter '$1' has a generic type", "because the parameter '$1' has a generic type",
errDestructorNotGenericEnough: "Destructor signarue is too specific. " & errDestructorNotGenericEnough: "Destructor signarue is too specific. " &
"A destructor must be associated will all instantiations of a generic type", "A destructor must be associated will all instantiations of a generic type",
errInlineIteratorsAsProcParams: "inline iterators can be used as parameters only for " &
"templates, macros and other inline iterators",
errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsTwoArguments: "\'$1\' expects two arguments",
errXExpectsObjectTypes: "\'$1\' expects object types", errXExpectsObjectTypes: "\'$1\' expects object types",
errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype",

View file

@ -139,6 +139,10 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
result = n.sym result = n.sym
internalAssert sfGenSym in result.flags internalAssert sfGenSym in result.flags
internalAssert result.kind == kind internalAssert result.kind == kind
# when there is a nested proc inside a template, semtmpl
# will assign a wrong owner during the first pass over the
# template; we must fix it here: see #909
result.owner = getCurrOwner()
else: else:
result = newSym(kind, considerAcc(n), getCurrOwner(), n.info) result = newSym(kind, considerAcc(n), getCurrOwner(), n.info)
@ -194,7 +198,8 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode =
result = semExprWithType(c, evaluated) result = semExprWithType(c, evaluated)
else: else:
result = evaluated result = evaluated
semmacrosanity.annotateType(result, eOrig.typ) let expectedType = eOrig.typ.skipTypes({tyStatic})
semmacrosanity.annotateType(result, expectedType)
else: else:
result = semExprWithType(c, evaluated) result = semExprWithType(c, evaluated)
#result = fitNode(c, e.typ, result) inlined with special case: #result = fitNode(c, e.typ, result) inlined with special case:

View file

@ -82,7 +82,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
# fail fast: # fail fast:
globalError(n.info, errTypeMismatch, "") globalError(n.info, errTypeMismatch, "")
var result = msgKindToString(errTypeMismatch) var result = msgKindToString(errTypeMismatch)
add(result, describeArgs(c, n, 1 + ord(nfDotField in n.flags))) add(result, describeArgs(c, n, 1))
add(result, ')') add(result, ')')
var candidates = "" var candidates = ""
@ -175,9 +175,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
localError(n.info, errExprXCannotBeCalled, localError(n.info, errExprXCannotBeCalled,
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))
else: else:
if {nfDotField, nfDotSetter} * n.flags != {}:
# clean up the inserted ops
n.sons.delete(2)
n.sons[0] = f
errors = @[] errors = @[]
pickBest(f) pickBest(f)
notFoundError(c, n, errors) notFoundError(c, n, errors)
return return
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and if alt.state == csMatch and cmpCandidates(result, alt) == 0 and

View file

@ -899,6 +899,21 @@ proc makeDeref(n: PNode): PNode =
addSon(result, a) addSon(result, a)
t = skipTypes(t.sons[0], {tyGenericInst}) t = skipTypes(t.sons[0], {tyGenericInst})
proc readTypeParameter(c: PContext, ty: PType,
paramName: PIdent, info: TLineInfo): PNode =
internalAssert ty.kind == tyGenericInst
let ty = ty.skipGenericAlias
let tbody = ty.sons[0]
for s in countup(0, tbody.len-2):
let tParam = tbody.sons[s]
if tParam.sym.name == paramName:
let rawTyp = ty.sons[s + 1]
if rawTyp.kind == tyStatic:
return rawTyp.n
else:
let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), info)
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
## returns nil if it's not a built-in field access ## returns nil if it's not a built-in field access
checkSonsLen(n, 2) checkSonsLen(n, 2)
@ -916,7 +931,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
var ty = n.sons[0].typ var ty = n.sons[0].typ
var f: PSym = nil var f: PSym = nil
result = nil result = nil
if isTypeExpr(n.sons[0]) or ty.kind == tyTypeDesc and ty.base.kind != tyNone: if isTypeExpr(n.sons[0]) or (ty.kind == tyTypeDesc and ty.base.kind != tyNone):
if ty.kind == tyTypeDesc: ty = ty.base if ty.kind == tyTypeDesc: ty = ty.base
case ty.kind case ty.kind
of tyEnum: of tyEnum:
@ -932,21 +947,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
markUsed(n, f) markUsed(n, f)
return return
of tyGenericInst: of tyGenericInst:
assert ty.sons[0].kind == tyGenericBody return readTypeParameter(c, ty, i, n.info)
let tbody = ty.sons[0]
for s in countup(0, tbody.len-2):
let tParam = tbody.sons[s]
if tParam.sym.name == i:
let rawTyp = ty.sons[s + 1]
if rawTyp.kind == tyStatic:
return rawTyp.n
else:
let foundTyp = makeTypeDesc(c, rawTyp)
return newSymNode(copySym(tParam.sym).linkTo(foundTyp), n.info)
return
of tyObject, tyTuple: of tyObject, tyTuple:
if ty.n.kind == nkRecList: if ty.n.kind == nkRecList:
for field in ty.n.sons: for field in ty.n:
if field.sym.name == i: if field.sym.name == i:
n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ]) n.typ = newTypeWithSons(c, tyFieldAccessor, @[ty, field.sym.typ])
n.typ.n = copyTree(n) n.typ.n = copyTree(n)
@ -958,6 +962,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# XXX: This is probably not relevant any more # XXX: This is probably not relevant any more
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
ty = n.sons[0].typ ty = n.sons[0].typ
return nil
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef}) ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef})
var check: PNode = nil var check: PNode = nil
@ -990,6 +995,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
n.typ = f.typ n.typ = f.typ
result = n result = n
# we didn't find any field, let's look for a generic param
if result == nil and n.sons[0].typ.kind == tyGenericInst:
result = readTypeParameter(c, n.sons[0].typ, i, n.info)
proc dotTransformation(c: PContext, n: PNode): PNode = proc dotTransformation(c: PContext, n: PNode): PNode =
if isSymChoice(n.sons[1]): if isSymChoice(n.sons[1]):
result = newNodeI(nkDotCall, n.info) result = newNodeI(nkDotCall, n.info)
@ -1119,6 +1128,9 @@ proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
n.sons[0] = x # 'result[]' --> 'result' n.sons[0] = x # 'result[]' --> 'result'
n.sons[1] = takeImplicitAddr(c, ri) n.sons[1] = takeImplicitAddr(c, ri)
template resultTypeIsInferrable(typ: PType): expr =
typ.isMetaType and typ.kind != tyTypeDesc
proc semAsgn(c: PContext, n: PNode): PNode = proc semAsgn(c: PContext, n: PNode): PNode =
checkSonsLen(n, 2) checkSonsLen(n, 2)
var a = n.sons[0] var a = n.sons[0]
@ -1170,7 +1182,7 @@ proc semAsgn(c: PContext, n: PNode): PNode =
if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: {efAllowDestructor} else: {})
if lhsIsResult: if lhsIsResult:
n.typ = enforceVoidContext n.typ = enforceVoidContext
if lhs.sym.typ.isMetaType and lhs.sym.typ.kind != tyTypeDesc: if resultTypeIsInferrable(lhs.sym.typ):
if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
internalAssert c.p.resultSym != nil internalAssert c.p.resultSym != nil
lhs.typ = rhs.typ lhs.typ = rhs.typ
@ -1259,12 +1271,21 @@ proc semYield(c: PContext, n: PNode): PNode =
localError(n.info, errYieldNotAllowedInTryStmt) localError(n.info, errYieldNotAllowedInTryStmt)
elif n.sons[0].kind != nkEmpty: elif n.sons[0].kind != nkEmpty:
n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility:
var restype = c.p.owner.typ.sons[0] var iterType = c.p.owner.typ
var restype = iterType.sons[0]
if restype != nil: if restype != nil:
let adjustedRes = if c.p.owner.kind == skIterator: restype.base let adjustedRes = if c.p.owner.kind == skIterator: restype.base
else: restype else: restype
n.sons[0] = fitNode(c, adjustedRes, n.sons[0]) n.sons[0] = fitNode(c, adjustedRes, n.sons[0])
if n.sons[0].typ == nil: internalError(n.info, "semYield") if n.sons[0].typ == nil: internalError(n.info, "semYield")
if resultTypeIsInferrable(adjustedRes):
let inferred = n.sons[0].typ
if c.p.owner.kind == skIterator:
iterType.sons[0].sons[0] = inferred
else:
iterType.sons[0] = inferred
semYieldVarResult(c, n, adjustedRes) semYieldVarResult(c, n, adjustedRes)
else: else:
localError(n.info, errCannotReturnExpr) localError(n.info, errCannotReturnExpr)
@ -1346,7 +1367,7 @@ proc expectString(c: PContext, n: PNode): string =
localError(n.info, errStringLiteralExpected) localError(n.info, errStringLiteralExpected)
proc getMagicSym(magic: TMagic): PSym = proc getMagicSym(magic: TMagic): PSym =
result = newSym(skProc, getIdent($magic), getCurrOwner(), gCodegenLineInfo) result = newSym(skProc, getIdent($magic), systemModule, gCodegenLineInfo)
result.magic = magic result.magic = magic
proc newAnonSym(kind: TSymKind, info: TLineInfo, proc newAnonSym(kind: TSymKind, info: TLineInfo,
@ -1939,7 +1960,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkBracketExpr: of nkBracketExpr:
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared}) var s = qualifiedLookUp(c, n.sons[0], {checkUndeclared})
if s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators: if (s != nil and s.kind in {skProc, skMethod, skConverter}+skIterators) or
n[0].kind in nkSymChoices:
# type parameters: partial generic specialization # type parameters: partial generic specialization
n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s) n.sons[0] = semSymGenericInstantiation(c, n.sons[0], s)
result = explicitGenericInstantiation(c, n, s) result = explicitGenericInstantiation(c, n, s)

View file

@ -20,7 +20,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
if a.kind != nkSym: if a.kind != nkSym:
internalError(a.info, "instantiateGenericParamList; no symbol") internalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses: if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
continue continue
var s = newSym(skType, q.name, getCurrOwner(), q.info) var s = newSym(skType, q.name, getCurrOwner(), q.info)
s.flags = s.flags + {sfUsed, sfFromGeneric} s.flags = s.flags + {sfUsed, sfFromGeneric}

View file

@ -661,10 +661,18 @@ proc semFor(c: PContext, n: PNode): PNode =
openScope(c) openScope(c)
n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator}) n.sons[length-2] = semExprNoDeref(c, n.sons[length-2], {efWantIterator})
var call = n.sons[length-2] var call = n.sons[length-2]
if call.kind in nkCallKinds and call.sons[0].typ.callConv == ccClosure: let isCallExpr = call.kind in nkCallKinds
if isCallExpr and call.sons[0].sym.magic != mNone:
if call.sons[0].sym.magic == mOmpParFor:
result = semForVars(c, n)
result.kind = nkParForStmt
else:
result = semForFields(c, n, call.sons[0].sym.magic)
elif (isCallExpr and call.sons[0].typ.callConv == ccClosure) or
call.typ.kind == tyIter:
# first class iterator: # first class iterator:
result = semForVars(c, n) result = semForVars(c, n)
elif call.kind notin nkCallKinds or call.sons[0].kind != nkSym or elif not isCallExpr or call.sons[0].kind != nkSym or
call.sons[0].sym.kind notin skIterators: call.sons[0].sym.kind notin skIterators:
if length == 3: if length == 3:
n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2]) n.sons[length-2] = implicitIterator(c, "items", n.sons[length-2])
@ -673,12 +681,6 @@ proc semFor(c: PContext, n: PNode): PNode =
else: else:
localError(n.sons[length-2].info, errIteratorExpected) localError(n.sons[length-2].info, errIteratorExpected)
result = semForVars(c, n) result = semForVars(c, n)
elif call.sons[0].sym.magic != mNone:
if call.sons[0].sym.magic == mOmpParFor:
result = semForVars(c, n)
result.kind = nkParForStmt
else:
result = semForFields(c, n, call.sons[0].sym.magic)
else: else:
result = semForVars(c, n) result = semForVars(c, n)
# propagate any enforced VoidContext: # propagate any enforced VoidContext:

View file

@ -348,7 +348,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
of nkMethodDef: of nkMethodDef:
result = semRoutineInTemplBody(c, n, skMethod) result = semRoutineInTemplBody(c, n, skMethod)
of nkIteratorDef: of nkIteratorDef:
result = semRoutineInTemplBody(c, n, n[namePos].sym.kind) let kind = if hasPragma(n[pragmasPos], wClosure): skClosureIterator
else: skIterator
result = semRoutineInTemplBody(c, n, kind)
of nkTemplateDef: of nkTemplateDef:
result = semRoutineInTemplBody(c, n, skTemplate) result = semRoutineInTemplBody(c, n, skTemplate)
of nkMacroDef: of nkMacroDef:

View file

@ -154,19 +154,31 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
result.n = newNodeI(nkRange, n.info) result.n = newNodeI(nkRange, n.info)
if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty):
localError(n.info, errRangeIsEmpty) localError(n.info, errRangeIsEmpty)
var a = semConstExpr(c, n[1])
var b = semConstExpr(c, n[2]) var range: array[2, PNode]
if not sameType(a.typ, b.typ): range[0] = semExprWithType(c, n[1], {efDetermineType})
range[1] = semExprWithType(c, n[2], {efDetermineType})
var rangeT: array[2, PType]
for i in 0..1: rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit
if not sameType(rangeT[0], rangeT[1]):
localError(n.info, errPureTypeMismatch) localError(n.info, errPureTypeMismatch)
elif a.typ.kind notin {tyInt..tyInt64,tyEnum,tyBool,tyChar, elif not rangeT[0].isOrdinalType:
tyFloat..tyFloat128,tyUInt8..tyUInt32}:
localError(n.info, errOrdinalTypeExpected) localError(n.info, errOrdinalTypeExpected)
elif enumHasHoles(a.typ): elif enumHasHoles(rangeT[0]):
localError(n.info, errEnumXHasHoles, a.typ.sym.name.s) localError(n.info, errEnumXHasHoles, rangeT[0].sym.name.s)
elif not leValue(a, b): localError(n.info, errRangeIsEmpty)
addSon(result.n, a) for i in 0..1:
addSon(result.n, b) if hasGenericArguments(range[i]):
addSonSkipIntLit(result, b.typ) result.n.addSon makeStaticExpr(c, range[i])
else:
result.n.addSon semConstExpr(c, range[i])
if weakLeValue(result.n[0], result.n[1]) == impNo:
localError(n.info, errRangeIsEmpty)
addSonSkipIntLit(result, rangeT[0])
proc semRange(c: PContext, n: PNode, prev: PType): PType = proc semRange(c: PContext, n: PNode, prev: PType): PType =
result = nil result = nil
@ -602,15 +614,24 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
incl(result.flags, tfFinal) incl(result.flags, tfFinal)
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
if kind == skMacro and param.typ.kind notin {tyTypeDesc, tyStatic}: template addDecl(x) =
# within a macro, every param has the type PNimrodNode! if sfGenSym notin x.flags: addDecl(c, x)
# and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
let nn = getSysSym"PNimrodNode" if kind == skMacro:
var a = copySym(param) if param.typ.kind == tyTypeDesc:
a.typ = nn.typ addDecl(param)
if sfGenSym notin a.flags: addDecl(c, a) elif param.typ.kind == tyStatic:
var a = copySym(param)
a.typ = param.typ.base
addDecl(a)
else:
# within a macro, every param has the type PNimrodNode!
let nn = getSysSym"PNimrodNode"
var a = copySym(param)
a.typ = nn.typ
addDecl(a)
else: else:
if sfGenSym notin param.flags: addDecl(c, param) addDecl(param)
let typedescId = getIdent"typedesc" let typedescId = getIdent"typedesc"
@ -722,6 +743,15 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result]) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
result = addImplicitGeneric(result) result = addImplicitGeneric(result)
of tyIter:
if paramType.callConv == ccInline:
if procKind notin {skTemplate, skMacro, skIterator}:
localError(info, errInlineIteratorsAsProcParams)
if paramType.len == 1:
let lifted = liftingWalk(paramType.base)
if lifted != nil: paramType.sons[0] = lifted
result = addImplicitGeneric(paramType)
of tyGenericInst: of tyGenericInst:
if paramType.lastSon.kind == tyUserTypeClass: if paramType.lastSon.kind == tyUserTypeClass:
var cp = copyType(paramType, getCurrOwner(), false) var cp = copyType(paramType, getCurrOwner(), false)
@ -840,9 +870,13 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
addParamOrResult(c, arg, kind) addParamOrResult(c, arg, kind)
if gCmd == cmdPretty: checkDef(a.sons[j], arg) if gCmd == cmdPretty: checkDef(a.sons[j], arg)
var r: PType
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
var r = semTypeNode(c, n.sons[0], nil) r = semTypeNode(c, n.sons[0], nil)
elif kind == skIterator:
r = newTypeS(tyAnything, c)
if r != nil:
# turn explicit 'void' return type into 'nil' because the rest of the # turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil': # compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst}).kind != tyEmpty: if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
@ -852,7 +886,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if lifted != nil: r = lifted if lifted != nil: r = lifted
r.flags.incl tfRetType r.flags.incl tfRetType
r = skipIntLit(r) r = skipIntLit(r)
if kind == skIterator: r = newTypeWithSons(c, tyIter, @[r]) if kind == skIterator:
# see tchainediterators
# in cases like iterator foo(it: iterator): type(it)
# we don't need to change the return type to iter[T]
if not r.isInlineIterator: r = newTypeWithSons(c, tyIter, @[r])
result.sons[0] = r result.sons[0] = r
res.typ = r res.typ = r
@ -984,7 +1022,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
of nkTypeOfExpr: of nkTypeOfExpr:
# for ``type(countup(1,3))``, see ``tests/ttoseq``. # for ``type(countup(1,3))``, see ``tests/ttoseq``.
checkSonsLen(n, 1) checkSonsLen(n, 1)
result = semExprWithType(c, n.sons[0], {efInTypeof}).typ.skipTypes({tyIter}) let typExpr = semExprWithType(c, n.sons[0], {efInTypeof})
result = typExpr.typ.skipTypes({tyIter})
of nkPar: of nkPar:
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else: else:
@ -1103,8 +1142,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = newConstraint(c, tyIter) result = newConstraint(c, tyIter)
else: else:
result = semProcTypeWithScope(c, n, prev, skClosureIterator) result = semProcTypeWithScope(c, n, prev, skClosureIterator)
result.flags.incl(tfIterator) if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
result.callConv = ccClosure result.kind = tyIter
result.callConv = ccInline
else:
result.flags.incl(tfIterator)
result.callConv = ccClosure
of nkProcTy: of nkProcTy:
if n.sonsLen == 0: if n.sonsLen == 0:
result = newConstraint(c, tyProc) result = newConstraint(c, tyProc)

View file

@ -181,7 +181,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
of nkStaticExpr: of nkStaticExpr:
var n = prepareNode(cl, n) var n = prepareNode(cl, n)
n = reResolveCallsWithTypedescParams(cl, n) n = reResolveCallsWithTypedescParams(cl, n)
result = cl.c.semExpr(cl.c, n) result = if cl.allowMetaTypes: n
else: cl.c.semExpr(cl.c, n)
else: else:
var length = sonsLen(n) var length = sonsLen(n)
if length > 0: if length > 0:
@ -306,6 +307,11 @@ proc skipIntLiteralParams(t: PType) =
t.sons[i] = skipped t.sons[i] = skipped
if i > 0: t.n.sons[i].sym.typ = skipped if i > 0: t.n.sons[i].sym.typ = skipped
# when the typeof operator is used on a static input
# param, the results gets infected with static as well:
if t.sons[0] != nil and t.sons[0].kind == tyStatic:
t.sons[0] = t.sons[0].base
proc propagateFieldFlags(t: PType, n: PNode) = proc propagateFieldFlags(t: PType, n: PNode) =
# This is meant for objects and tuples # This is meant for objects and tuples
# The type must be fully instantiated! # The type must be fully instantiated!
@ -314,16 +320,15 @@ proc propagateFieldFlags(t: PType, n: PNode) =
of nkSym: of nkSym:
propagateToOwner(t, n.sym.typ) propagateToOwner(t, n.sym.typ)
of nkRecList, nkRecCase, nkOfBranch, nkElse: of nkRecList, nkRecCase, nkOfBranch, nkElse:
if n.sons != nil: for son in n:
for son in n.sons: propagateFieldFlags(t, son)
propagateFieldFlags(t, son)
else: discard else: discard
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result = t result = t
if t == nil: return if t == nil: return
if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses: if t.kind in {tyStatic, tyGenericParam, tyIter} + tyTypeClasses:
let lookup = PType(idTableGet(cl.typeMap, t)) let lookup = PType(idTableGet(cl.typeMap, t))
if lookup != nil: return lookup if lookup != nil: return lookup
@ -336,6 +341,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result = replaceTypeVarsT(cl, lastSon(t)) result = replaceTypeVarsT(cl, lastSon(t))
of tyFromExpr: of tyFromExpr:
if cl.allowMetaTypes: return
var n = prepareNode(cl, t.n) var n = prepareNode(cl, t.n)
n = cl.c.semConstExpr(cl.c, n) n = cl.c.semConstExpr(cl.c, n)
if n.typ.kind == tyTypeDesc: if n.typ.kind == tyTypeDesc:

View file

@ -1014,6 +1014,10 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
result.typ = getInstantiatedType(c, arg, m, base(f)) result.typ = getInstantiatedType(c, arg, m, base(f))
m.baseTypeMatch = true m.baseTypeMatch = true
proc isInlineIterator*(t: PType): bool =
result = t.kind == tyIter or
(t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argSemantized, argOrig: PNode): PNode = argSemantized, argOrig: PNode): PNode =
var var
@ -1061,6 +1065,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
else: else:
return argOrig return argOrig
if r != isNone and f.isInlineIterator:
var inlined = newTypeS(tyStatic, c)
inlined.sons = @[argType]
inlined.n = argSemantized
put(m.bindings, f, inlined)
return argSemantized
case r case r
of isConvertible: of isConvertible:
inc(m.convMatches) inc(m.convMatches)
@ -1188,7 +1199,9 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
# a.typ == nil is valid # a.typ == nil is valid
result = a result = a
elif a.typ.isNil: elif a.typ.isNil:
result = c.semOperand(c, a, {efDetermineType}) let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
else: {efDetermineType}
result = c.semOperand(c, a, flags)
else: else:
result = a result = a

View file

@ -433,8 +433,9 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
var length = sonsLen(n) var length = sonsLen(n)
var call = n.sons[length - 2] var call = n.sons[length - 2]
if call.kind notin nkCallKinds or call.sons[0].kind != nkSym or if call.typ.kind != tyIter and
call.sons[0].sym.kind != skIterator: (call.kind notin nkCallKinds or call.sons[0].kind != nkSym or
call.sons[0].sym.kind != skIterator):
n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode n.sons[length-1] = transformLoopBody(c, n.sons[length-1]).PNode
return lambdalifting.liftForLoop(n).PTransNode return lambdalifting.liftForLoop(n).PTransNode
#InternalError(call.info, "transformFor") #InternalError(call.info, "transformFor")
@ -459,6 +460,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
for i in countup(1, sonsLen(call) - 1): for i in countup(1, sonsLen(call) - 1):
var arg = transform(c, call.sons[i]).PNode var arg = transform(c, call.sons[i]).PNode
var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym var formal = skipTypes(iter.typ, abstractInst).n.sons[i].sym
if arg.typ.kind == tyIter: continue
case putArgInto(arg, formal.typ) case putArgInto(arg, formal.typ)
of paDirectMapping: of paDirectMapping:
idNodeTablePut(newC.mapping, formal, arg) idNodeTablePut(newC.mapping, formal, arg)
@ -480,7 +482,7 @@ proc transformFor(c: PTransf, n: PNode): PTransNode =
dec(c.inlining) dec(c.inlining)
popInfoContext() popInfoContext()
popTransCon(c) popTransCon(c)
#echo "transformed: ", renderTree(n) # echo "transformed: ", result.PNode.renderTree
proc getMagicOp(call: PNode): TMagic = proc getMagicOp(call: PNode): TMagic =
if call.sons[0].kind == nkSym and if call.sons[0].kind == nkSym and

View file

@ -425,7 +425,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
decodeBC(rkNode) decodeBC(rkNode)
let src = regs[rb].node let src = regs[rb].node
if src.kind notin {nkEmpty..nkNilLit}: if src.kind notin {nkEmpty..nkNilLit}:
regs[ra].node = src.sons[rc] let n = src.sons[rc]
regs[ra].node = if n.kind == nkExprColonExpr: n[1]
else: n
else: else:
stackTrace(c, tos, pc, errIndexOutOfBounds) stackTrace(c, tos, pc, errIndexOutOfBounds)
of opcWrObj: of opcWrObj:

View file

@ -2875,7 +2875,6 @@ as there are components in the tuple. The i'th iteration variable's type is
the type of the i'th component. In other words, implicit tuple unpacking in a the type of the i'th component. In other words, implicit tuple unpacking in a
for loop context is supported. for loop context is supported.
Implict items/pairs invocations Implict items/pairs invocations
------------------------------- -------------------------------
@ -2900,10 +2899,11 @@ First class iterators
There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators. There are 2 kinds of iterators in Nimrod: *inline* and *closure* iterators.
An `inline iterator`:idx: is an iterator that's always inlined by the compiler An `inline iterator`:idx: is an iterator that's always inlined by the compiler
leading to zero overhead for the abstraction, but may result in a heavy leading to zero overhead for the abstraction, but may result in a heavy
increase in code size. Inline iterators are second class increase in code size. Inline iterators are second class citizens;
citizens; one cannot pass them around like first class procs. They can be passed as parameters only to other inlining code facilities like
templates, macros and other inline iterators.
In contrast to that, a `closure iterator`:idx: can be passed around: In contrast to that, a `closure iterator`:idx: can be passed around more freely:
.. code-block:: nimrod .. code-block:: nimrod
iterator count0(): int {.closure.} = iterator count0(): int {.closure.} =
@ -2926,9 +2926,7 @@ Closure iterators have other restrictions than inline iterators:
1. ``yield`` in a closure iterator can not occur in a ``try`` statement. 1. ``yield`` in a closure iterator can not occur in a ``try`` statement.
2. For now, a closure iterator cannot be evaluated at compile time. 2. For now, a closure iterator cannot be evaluated at compile time.
3. ``return`` is allowed in a closure iterator (but rarely useful). 3. ``return`` is allowed in a closure iterator (but rarely useful).
4. Since closure iterators can be used as a collaborative tasking 4. Both inline and closure iterators cannot be recursive.
system, ``void`` is a valid return type for them.
5. Both inline and closure iterators cannot be recursive.
Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly
default to being inline, but that this may change in future versions of the default to being inline, but that this may change in future versions of the
@ -2990,6 +2988,14 @@ parameters of an outer factory proc:
for f in foo(): for f in foo():
echo f echo f
Implicit return type
--------------------
Since inline interators must always produce values that will be consumed in
a for loop, the compiler will implicity use the ``auto`` return type if no
type is given by the user. In contrast, since closure iterators can be used
as a collaborative tasking system, ``void`` is a valid return type for them.
Type sections Type sections
============= =============
@ -4069,8 +4075,8 @@ Static params can also appear in the signatures of generic types:
AffineTransform2D[T] = Matrix[3, 3, T] AffineTransform2D[T] = Matrix[3, 3, T]
AffineTransform3D[T] = Matrix[4, 4, T] AffineTransform3D[T] = Matrix[4, 4, T]
AffineTransform3D[float] # OK var m1: AffineTransform3D[float] # OK
AffineTransform2D[string] # Error, `string` is not a `Number` var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
typedesc typedesc

View file

@ -195,8 +195,14 @@ proc open*(connection, user, password, database: string): TDbConn {.
## be established. ## be established.
result = mysql.Init(nil) result = mysql.Init(nil)
if result == nil: dbError("could not open database connection") if result == nil: dbError("could not open database connection")
if mysql.RealConnect(result, "", user, password, database, let
0'i32, nil, 0) == nil: colonPos = connection.find(':')
host = if colonPos < 0: connection
else: substr(connection, 0, colonPos-1)
port: int32 = if colonPos < 0: 0'i32
else: substr(connection, colonPos+1).parseInt.int32
if mysql.RealConnect(result, host, user, password, database,
port, nil, 0) == nil:
var errmsg = $mysql.error(result) var errmsg = $mysql.error(result)
db_mysql.Close(result) db_mysql.Close(result)
dbError(errmsg) dbError(errmsg)

View file

@ -314,6 +314,9 @@ static unsigned long nimNaN[2]={0xffffffff, 0x7fffffff};
# define INF INFINITY # define INF INFINITY
# elif defined(HUGE_VAL) # elif defined(HUGE_VAL)
# define INF HUGE_VAL # define INF HUGE_VAL
# elif defined(_MSC_VER)
# include <float.h>
# define INF (DBL_MAX+DBL_MAX)
# else # else
# define INF (1.0 / 0.0) # define INF (1.0 / 0.0)
# endif # endif

View file

@ -36,7 +36,7 @@ type
epoll_data* {.importc: "union epoll_data", epoll_data* {.importc: "union epoll_data",
header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union. header: "<sys/epoll.h>", pure, final.} = object # TODO: This is actually a union.
#thePtr* {.importc: "ptr".}: pointer #thePtr* {.importc: "ptr".}: pointer
fd*: cint # \ fd* {.importc: "fd".}: cint # \
#u32*: uint32 #u32*: uint32
#u64*: uint64 #u64*: uint64

View file

@ -473,7 +473,6 @@ else:
proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) = proc update(p: PDispatcher, sock: TSocketHandle, events: set[TEvent]) =
assert sock in p.selector assert sock in p.selector
echo("Update: ", events)
if events == {}: if events == {}:
discard p.selector.unregister(sock) discard p.selector.unregister(sock)
else: else:
@ -499,23 +498,25 @@ else:
for info in p.selector.select(timeout): for info in p.selector.select(timeout):
let data = PData(info.key.data) let data = PData(info.key.data)
assert data.sock == info.key.fd assert data.sock == info.key.fd
echo("R: ", data.readCBs.len, " W: ", data.writeCBs.len, ". ", info.events)
if EvRead in info.events: if EvRead in info.events:
var newReadCBs: seq[TCallback] = @[] # Callback may add items to ``data.readCBs`` which causes issues if
for cb in data.readCBs: # we are iterating over ``data.readCBs`` at the same time. We therefore
# make a copy to iterate over.
let currentCBs = data.readCBs
data.readCBs = @[]
for cb in currentCBs:
if not cb(data.sock): if not cb(data.sock):
# Callback wants to be called again. # Callback wants to be called again.
newReadCBs.add(cb) data.readCBs.add(cb)
data.readCBs = newReadCBs
if EvWrite in info.events: if EvWrite in info.events:
var newWriteCBs: seq[TCallback] = @[] let currentCBs = data.writeCBs
for cb in data.writeCBs: data.writeCBs = @[]
for cb in currentCBs:
if not cb(data.sock): if not cb(data.sock):
# Callback wants to be called again. # Callback wants to be called again.
newWriteCBs.add(cb) data.writeCBs.add(cb)
data.writeCBs = newWriteCBs
var newEvents: set[TEvent] var newEvents: set[TEvent]
if data.readCBs.len != 0: newEvents = {EvRead} if data.readCBs.len != 0: newEvents = {EvRead}
@ -616,7 +617,6 @@ else:
addWrite(p, socket, cb) addWrite(p, socket, cb)
return retFuture return retFuture
proc acceptAddr*(p: PDispatcher, socket: TSocketHandle): proc acceptAddr*(p: PDispatcher, socket: TSocketHandle):
PFuture[tuple[address: string, client: TSocketHandle]] = PFuture[tuple[address: string, client: TSocketHandle]] =
var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]() var retFuture = newFuture[tuple[address: string, client: TSocketHandle]]()
@ -854,7 +854,7 @@ when isMainModule:
sock.setBlocking false sock.setBlocking false
when false: when true:
# Await tests # Await tests
proc main(p: PDispatcher): PFuture[int] {.async.} = proc main(p: PDispatcher): PFuture[int] {.async.} =
discard await p.connect(sock, "irc.freenode.net", TPort(6667)) discard await p.connect(sock, "irc.freenode.net", TPort(6667))
@ -880,7 +880,7 @@ when isMainModule:
else: else:
when false: when true:
var f = p.connect(sock, "irc.freenode.org", TPort(6667)) var f = p.connect(sock, "irc.freenode.org", TPort(6667))
f.callback = f.callback =

View file

@ -10,11 +10,13 @@
# TODO: Docs. # TODO: Docs.
import tables, os, unsigned, hashes import tables, os, unsigned, hashes
import sockets2
when defined(linux): import posix, epoll when defined(linux): import posix, epoll
elif defined(windows): import winlean elif defined(windows): import winlean
proc hash*(x: TSocketHandle): THash {.borrow.} proc hash*(x: TSocketHandle): THash {.borrow.}
proc `$`*(x: TSocketHandle): string {.borrow.}
type type
TEvent* = enum TEvent* = enum
@ -31,7 +33,7 @@ when defined(linux) or defined(nimdoc):
type type
PSelector* = ref object PSelector* = ref object
epollFD: cint epollFD: cint
events: array[64, ptr epoll_event] events: array[64, epoll_event]
fds: TTable[TSocketHandle, PSelectorKey] fds: TTable[TSocketHandle, PSelectorKey]
proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event = proc createEventStruct(events: set[TEvent], fd: TSocketHandle): epoll_event =
@ -66,17 +68,25 @@ when defined(linux) or defined(nimdoc):
var event = createEventStruct(events, fd) var event = createEventStruct(events, fd)
s.fds[fd].events = events s.fds[fd].events = events
echo("About to update")
if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_MOD, fd, addr(event)) != 0:
if OSLastError().cint == ENOENT:
# Socket has been closed. Epoll automatically removes disconnected
# sockets.
s.fds.del(fd)
osError("Socket has been disconnected")
OSError(OSLastError()) OSError(OSLastError())
echo("finished updating")
result = s.fds[fd] result = s.fds[fd]
proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} = proc unregister*(s: PSelector, fd: TSocketHandle): PSelectorKey {.discardable.} =
if not s.fds.hasKey(fd): if not s.fds.hasKey(fd):
raise newException(EInvalidValue, "File descriptor not found.") raise newException(EInvalidValue, "File descriptor not found.")
if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0: if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fd, nil) != 0:
OSError(OSLastError()) if osLastError().cint == ENOENT:
# Socket has been closed. Epoll automatically removes disconnected
# sockets so its already been removed.
else:
OSError(OSLastError())
result = s.fds[fd] result = s.fds[fd]
s.fds.del(fd) s.fds.del(fd)
@ -92,21 +102,21 @@ when defined(linux) or defined(nimdoc):
## on the ``fd``. ## on the ``fd``.
result = @[] result = @[]
let evNum = epoll_wait(s.epollFD, s.events[0], 64.cint, timeout.cint) let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
if evNum < 0: OSError(OSLastError()) if evNum < 0: OSError(OSLastError())
if evNum == 0: return @[] if evNum == 0: return @[]
for i in 0 .. <evNum: for i in 0 .. <evNum:
var evSet: set[TEvent] = {} var evSet: set[TEvent] = {}
if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead} if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite} if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle] let selectorKey = s.fds[s.events[i].data.fd.TSocketHandle]
assert selectorKey != nil
result.add((selectorKey, evSet)) result.add((selectorKey, evSet))
proc newSelector*(): PSelector = proc newSelector*(): PSelector =
new result new result
result.epollFD = epoll_create(64) result.epollFD = epoll_create(64)
result.events = cast[array[64, ptr epoll_event]](alloc0(sizeof(epoll_event)*64)) result.events = cast[array[64, epoll_event]](alloc0(sizeof(epoll_event)*64))
result.fds = initTable[TSocketHandle, PSelectorKey]() result.fds = initTable[TSocketHandle, PSelectorKey]()
if result.epollFD < 0: if result.epollFD < 0:
OSError(OSLastError()) OSError(OSLastError())

View file

@ -24,6 +24,10 @@ else:
export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen, export TSocketHandle, TSockaddr_in, TAddrinfo, INADDR_ANY, TSockAddr, TSockLen,
inet_ntoa, recv, `==`, connect, send, accept inet_ntoa, recv, `==`, connect, send, accept
export
SO_ERROR,
SOL_SOCKET
type type
TPort* = distinct uint16 ## port type TPort* = distinct uint16 ## port type
@ -208,6 +212,24 @@ proc htons*(x: int16): int16 =
## order, this is a no-op; otherwise, it performs a 2-byte swap operation. ## order, this is a no-op; otherwise, it performs a 2-byte swap operation.
result = sockets2.ntohs(x) result = sockets2.ntohs(x)
proc getSockOptInt*(socket: TSocketHandle, level, optname: int): int {.
tags: [FReadIO].} =
## getsockopt for integer options.
var res: cint
var size = sizeof(res).TSocklen
if getsockopt(socket, cint(level), cint(optname),
addr(res), addr(size)) < 0'i32:
osError(osLastError())
result = int(res)
proc setSockOptInt*(socket: TSocketHandle, level, optname, optval: int) {.
tags: [FWriteIO].} =
## setsockopt for integer options.
var value = cint(optval)
if setsockopt(socket, cint(level), cint(optname), addr(value),
sizeof(value).TSocklen) < 0'i32:
osError(osLastError())
when defined(Windows): when defined(Windows):
var wsa: TWSADATA var wsa: TWSADATA
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError()) if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())

View file

@ -1164,13 +1164,13 @@ when not defined(nimrodVM):
## from it before writing to it is undefined behaviour! ## from it before writing to it is undefined behaviour!
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `allocShared` to allocate from a shared heap. ## Use `allocShared` to allocate from a shared heap.
proc alloc*(T: typedesc, size = 1): ptr T {.inline.} = proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
## allocates a new memory block with at least ``T.sizeof * size`` ## allocates a new memory block with at least ``T.sizeof * size``
## bytes. The block has to be freed with ``realloc(block, 0)`` or ## bytes. The block has to be freed with ``resize(block, 0)`` or
## ``dealloc(block)``. The block is not initialized, so reading ## ``free(block)``. The block is not initialized, so reading
## from it before writing to it is undefined behaviour! ## from it before writing to it is undefined behaviour!
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `allocShared` to allocate from a shared heap. ## Use `createSharedU` to allocate from a shared heap.
cast[ptr T](alloc(T.sizeof * size)) cast[ptr T](alloc(T.sizeof * size))
proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].} proc alloc0*(size: int): pointer {.noconv, rtl, tags: [].}
## allocates a new memory block with at least ``size`` bytes. The ## allocates a new memory block with at least ``size`` bytes. The
@ -1179,13 +1179,13 @@ when not defined(nimrodVM):
## containing zero, so it is somewhat safer than ``alloc``. ## containing zero, so it is somewhat safer than ``alloc``.
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `allocShared0` to allocate from a shared heap. ## Use `allocShared0` to allocate from a shared heap.
proc alloc0*(T: typedesc, size = 1): ptr T {.inline.} = proc create*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
## allocates a new memory block with at least ``T.sizeof * size`` ## allocates a new memory block with at least ``T.sizeof * size``
## bytes. The block has to be freed with ``realloc(block, 0)`` or ## bytes. The block has to be freed with ``resize(block, 0)`` or
## ``dealloc(block)``. The block is initialized with all bytes ## ``free(block)``. The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``alloc``. ## containing zero, so it is somewhat safer than ``createU``.
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `allocShared0` to allocate from a shared heap. ## Use `createShared` to allocate from a shared heap.
cast[ptr T](alloc0(T.sizeof * size)) cast[ptr T](alloc0(T.sizeof * size))
proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].} proc realloc*(p: pointer, newSize: int): pointer {.noconv, rtl, tags: [].}
## grows or shrinks a given memory block. If p is **nil** then a new ## grows or shrinks a given memory block. If p is **nil** then a new
@ -1195,14 +1195,14 @@ when not defined(nimrodVM):
## be freed with ``dealloc``. ## be freed with ``dealloc``.
## The allocated memory belongs to its allocating thread! ## The allocated memory belongs to its allocating thread!
## Use `reallocShared` to reallocate from a shared heap. ## Use `reallocShared` to reallocate from a shared heap.
proc reallocType*[T](p: ptr T, newSize: int): ptr T {.inline.} = proc resize*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
## grows or shrinks a given memory block. If p is **nil** then a new ## grows or shrinks a given memory block. If p is **nil** then a new
## memory block is returned. In either way the block has at least ## memory block is returned. In either way the block has at least
## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not
## **nil** ``realloc`` calls ``dealloc(p)``. In other cases the block ## **nil** ``resize`` calls ``free(p)``. In other cases the block
## has to be freed with ``dealloc``. The allocated memory belongs to ## has to be freed with ``free``. The allocated memory belongs to
## its allocating thread! ## its allocating thread!
## Use `reallocShared` to reallocate from a shared heap. ## Use `resizeShared` to reallocate from a shared heap.
cast[ptr T](realloc(p, T.sizeof * newSize)) cast[ptr T](realloc(p, T.sizeof * newSize))
proc dealloc*(p: pointer) {.noconv, rtl, tags: [].} proc dealloc*(p: pointer) {.noconv, rtl, tags: [].}
## frees the memory allocated with ``alloc``, ``alloc0`` or ## frees the memory allocated with ``alloc``, ``alloc0`` or
@ -1212,16 +1212,18 @@ when not defined(nimrodVM):
## or other memory may be corrupted. ## or other memory may be corrupted.
## The freed memory must belong to its allocating thread! ## The freed memory must belong to its allocating thread!
## Use `deallocShared` to deallocate from a shared heap. ## Use `deallocShared` to deallocate from a shared heap.
proc free*[T](p: ptr T) {.inline.} =
dealloc(p)
proc allocShared*(size: int): pointer {.noconv, rtl.} proc allocShared*(size: int): pointer {.noconv, rtl.}
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``size`` bytes. The block has to be freed with ## least ``size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block
## is not initialized, so reading from it before writing to it is ## is not initialized, so reading from it before writing to it is
## undefined behaviour! ## undefined behaviour!
proc allocShared*(T: typedesc, size: int): ptr T {.inline.} = proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with ## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. The block ## ``resizeShared(block, 0)`` or ``freeShared(block)``. The block
## is not initialized, so reading from it before writing to it is ## is not initialized, so reading from it before writing to it is
## undefined behaviour! ## undefined behaviour!
cast[ptr T](allocShared(T.sizeof * size)) cast[ptr T](allocShared(T.sizeof * size))
@ -1231,25 +1233,25 @@ when not defined(nimrodVM):
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## ``reallocShared(block, 0)`` or ``deallocShared(block)``.
## The block is initialized with all bytes ## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``. ## containing zero, so it is somewhat safer than ``allocShared``.
proc allocShared0*(T: typedesc, size: int): ptr T {.inline.} = proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} =
## allocates a new memory block on the shared heap with at ## allocates a new memory block on the shared heap with at
## least ``T.sizeof * size`` bytes. The block has to be freed with ## least ``T.sizeof * size`` bytes. The block has to be freed with
## ``reallocShared(block, 0)`` or ``deallocShared(block)``. ## ``resizeShared(block, 0)`` or ``freeShared(block)``.
## The block is initialized with all bytes ## The block is initialized with all bytes
## containing zero, so it is somewhat safer than ``allocShared``. ## containing zero, so it is somewhat safer than ``createSharedU``.
cast[ptr T](allocShared(T.sizeof * size)) cast[ptr T](allocShared0(T.sizeof * size))
proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.} proc reallocShared*(p: pointer, newSize: int): pointer {.noconv, rtl.}
## grows or shrinks a given memory block on the heap. If p is **nil** ## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at ## then a new memory block is returned. In either way the block has at
## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil** ## least ``newSize`` bytes. If ``newSize == 0`` and p is not **nil**
## ``reallocShared`` calls ``deallocShared(p)``. In other cases the ## ``reallocShared`` calls ``deallocShared(p)``. In other cases the
## block has to be freed with ``deallocShared``. ## block has to be freed with ``deallocShared``.
proc reallocSharedType*[T](p: ptr T, newSize: int): ptr T {.inline.} = proc resizeShared*[T](p: ptr T, newSize: Natural): ptr T {.inline.} =
## grows or shrinks a given memory block on the heap. If p is **nil** ## grows or shrinks a given memory block on the heap. If p is **nil**
## then a new memory block is returned. In either way the block has at ## then a new memory block is returned. In either way the block has at
## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is ## least ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is
## not **nil** ``reallocShared`` calls ``deallocShared(p)``. In other ## not **nil** ``resizeShared`` calls ``freeShared(p)``. In other
## cases the block has to be freed with ``deallocShared``. ## cases the block has to be freed with ``freeShared``.
cast[ptr T](reallocShared(p, T.sizeof * newSize)) cast[ptr T](reallocShared(p, T.sizeof * newSize))
proc deallocShared*(p: pointer) {.noconv, rtl.} proc deallocShared*(p: pointer) {.noconv, rtl.}
## frees the memory allocated with ``allocShared``, ``allocShared0`` or ## frees the memory allocated with ``allocShared``, ``allocShared0`` or
@ -1257,6 +1259,13 @@ when not defined(nimrodVM):
## free the memory a leak occurs; if one tries to access freed ## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen ## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted. ## or other memory may be corrupted.
proc freeShared*[T](p: ptr T) {.inline.} =
## frees the memory allocated with ``createShared``, ``createSharedU`` or
## ``resizeShared``. This procedure is dangerous! If one forgets to
## free the memory a leak occurs; if one tries to access freed
## memory (or just freeing it twice!) a core dump may happen
## or other memory may be corrupted.
deallocShared(p)
proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} 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

View file

@ -15,16 +15,24 @@ const
var clientCount = 0 var clientCount = 0
proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} = proc sendMessages(disp: PDispatcher, client: TSocketHandle): PFuture[int] {.async.} =
echo("entering sendMessages")
for i in 0 .. <messagesToSend: for i in 0 .. <messagesToSend:
discard await disp.send(client, "Message " & $i & "\c\L") discard await disp.send(client, "Message " & $i & "\c\L")
echo("returning sendMessages")
proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} = proc launchSwarm(disp: PDispatcher, port: TPort): PFuture[int] {.async.} =
for i in 0 .. <swarmSize: for i in 0 .. <swarmSize:
var sock = socket() var sock = socket()
# TODO: We may need to explicitly register and unregister the fd.
# This is because when the socket is closed, selectors is not aware
# that it has been closed. While epoll is. Perhaps we should just unregister
# in close()?
echo(sock.cint)
#disp.register(sock) #disp.register(sock)
discard await disp.connect(sock, "localhost", port) discard await disp.connect(sock, "localhost", port)
when true: when true:
discard await sendMessages(disp, sock) discard await sendMessages(disp, sock)
echo("Calling close")
sock.close() sock.close()
else: else:
# Issue #932: https://github.com/Araq/Nimrod/issues/932 # Issue #932: https://github.com/Araq/Nimrod/issues/932

View file

@ -14,7 +14,8 @@ macro selectType(a, b: typedesc): typedesc =
type type
Foo[T] = object Foo[T] = object
data1: array[T.high, int] data1: array[T.high, int]
data2: array[typeNameLen(T), float] # data3: array[0..T.typeNameLen, selectType(float, int)] data2: array[typeNameLen(T), float]
data3: array[0..T.typeNameLen, selectType(float, int)]
MyEnum = enum A, B, C, D MyEnum = enum A, B, C, D
@ -27,10 +28,15 @@ echo high(f1.data2) # (MyEnum.len = 6) - 1 == 5
echo high(f2.data1) # 127 - 1 == 126 echo high(f2.data1) # 127 - 1 == 126
echo high(f2.data2) # int8.len - 1 == 3 echo high(f2.data2) # int8.len - 1 == 3
#static: static:
# assert high(f1.data1) == ord(D) assert high(f1.data1) == ord(C)
# assert high(f1.data2) == 6 # length of MyEnum assert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
# assert high(f2.data1) == 127 assert high(f2.data1) == 126
# assert high(f2.data2) == 4 # length of int8 assert high(f2.data2) == 3
assert high(f1.data3) == 6 # length of MyEnum
assert high(f2.data3) == 4 # length of int8
assert f2.data3[0] is float

View file

@ -0,0 +1,38 @@
discard """
output: '''16
32
48
64
128
192
'''
"""
iterator gaz(it: iterator{.inline.}): type(it) =
for x in it:
yield x*2
iterator baz(it: iterator{.inline.}) =
for x in gaz(it):
yield x*2
type T1 = auto
iterator bar(it: iterator: T1{.inline.}): T1 =
for x in baz(it):
yield x*2
iterator foo[T](x: iterator: T{.inline.}): T =
for e in bar(x):
yield e*2
var s = @[1, 2, 3]
# pass an interator several levels deep:
for x in s.items.foo:
echo x
# use some complex iterator as an input for another one:
for x in s.items.baz.foo:
echo x

26
tests/iter/titerable.nim Normal file
View file

@ -0,0 +1,26 @@
discard """
output: '''2
4
6
4
8
12
'''
"""
iterator map[T, U](s: iterator:T{.inline.}, f: proc(x: T): U): U =
for e in s: yield f(e)
template toSeq(s: expr): expr =
var res = newSeq[type(s)](0)
for e in s: res.add(e)
res
var s1 = @[1, 2, 3]
for x in map(s1.items, proc (a:int): int = a*2):
echo x
var s2 = toSeq(map(s1.items, proc (a:int): int = a*4))
for x in s2:
echo x

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "tstaticparams.nim" file: "tstaticparams.nim"
output: "abracadabra\ntest\n3" output: "abracadabra\ntest\n3\n15\4"
""" """
type type
@ -11,8 +11,8 @@ type
data: array[I, T] data: array[I, T]
TA1[T; I: static[int]] = array[I, T] TA1[T; I: static[int]] = array[I, T]
# TA2[T; I: static[int]] = array[0..I, T] TA2[T; I: static[int]] = array[0..I, T]
# TA3[T; I: static[int]] = array[I-1, T] TA3[T; I: static[int]] = array[I-1, T]
proc takeFoo(x: TFoo) = proc takeFoo(x: TFoo) =
echo "abracadabra" echo "abracadabra"
@ -26,6 +26,22 @@ echo high(y.data)
var var
t1: TA1[float, 1] t1: TA1[float, 1]
# t2: TA2[string, 4] t2: TA2[string, 4]
# t3: TA3[int, 10] t3: TA3[int, 10]
# example from the manual:
type
Matrix[M,N: static[int]; T] = array[0..(M*N - 1), T]
# Note how `Number` is just a type constraint here, while
# `static[int]` requires us to supply a compile-time int value
AffineTransform2D[T] = Matrix[3, 3, T]
AffineTransform3D[T] = Matrix[4, 4, T]
var m: AffineTransform3D[float]
echo high(m)
proc getRows(mtx: Matrix): int =
result = mtx.M
echo getRows(m)

View file

@ -0,0 +1,12 @@
discard """
msg: 'type mismatch: got (PTest)'
"""
type
PTest = ref object
proc test(x: PTest, y: int) = nil
var buf: PTest
buf.test()

View file

@ -0,0 +1,52 @@
discard """
msg: '''letters
aa
bb
numbers
11
22
AST a
[(11, 22), (33, 44)]
AST b
(e: [55, 66], f: [77, 88])
55
'''
"""
import macros
type
TConfig = tuple
letters: seq[string]
numbers:seq[int]
const data: Tconfig = (@["aa", "bb"], @[11, 22])
macro mymacro(data: static[TConfig]): stmt =
echo "letters"
for s in items(data.letters):
echo s
echo "numbers"
for n in items(data.numbers):
echo n
mymacro(data)
type
Ta = seq[tuple[c:int, d:int]]
Tb = tuple[e:seq[int], f:seq[int]]
const
a : Ta = @[(11, 22), (33, 44)]
b : Tb = (@[55,66], @[77, 88])
macro mA(data: static[Ta]): stmt =
echo "AST a \n", repr(data)
macro mB(data: static[Tb]): stmt =
echo "AST b \n", repr(data)
echo data.e[0]
mA(a)
mB(b)

View file

@ -2,44 +2,51 @@ var x: ptr int
x = cast[ptr int](alloc(7)) x = cast[ptr int](alloc(7))
assert x != nil assert x != nil
x = cast[ptr int](x.realloc(2))
x = alloc(int, 3)
assert x != nil assert x != nil
x.dealloc() x.dealloc()
x = alloc0(int, 4) x = createU(int, 3)
assert x != nil
x.free()
x = create(int, 4)
assert cast[ptr array[4, int]](x)[0] == 0 assert cast[ptr array[4, int]](x)[0] == 0
assert cast[ptr array[4, int]](x)[1] == 0 assert cast[ptr array[4, int]](x)[1] == 0
assert cast[ptr array[4, int]](x)[2] == 0 assert cast[ptr array[4, int]](x)[2] == 0
assert cast[ptr array[4, int]](x)[3] == 0 assert cast[ptr array[4, int]](x)[3] == 0
x = cast[ptr int](x.realloc(2)) x = x.resize(4)
assert x != nil assert x != nil
x.free()
x = x.reallocType(4)
assert x != nil
x.dealloc()
x = cast[ptr int](allocShared(100)) x = cast[ptr int](allocShared(100))
assert x != nil assert x != nil
deallocShared(x) deallocShared(x)
x = allocShared(int, 3) x = createSharedU(int, 3)
assert x != nil assert x != nil
x.deallocShared() x.freeShared()
x = allocShared0(int, 3) x = createShared(int, 3)
assert x != nil assert x != nil
assert cast[ptr array[3, int]](x)[0] == 0 assert cast[ptr array[3, int]](x)[0] == 0
assert cast[ptr array[3, int]](x)[1] == 0 assert cast[ptr array[3, int]](x)[1] == 0
assert cast[ptr array[3, int]](x)[2] == 0 assert cast[ptr array[3, int]](x)[2] == 0
x = cast[ptr int](reallocShared(x, 2))
assert x != nil assert x != nil
x = cast[ptr int](x.resizeShared(2))
assert x != nil
x.freeShared()
x = reallocType(x, 12) x = create(int, 10)
assert x != nil assert x != nil
x = x.resize(12)
assert x != nil
x.dealloc()
x = reallocSharedType(x, 1) x = createShared(int, 1)
assert x != nil assert x != nil
x.deallocShared() x = x.resizeShared(1)
assert x != nil
x.freeShared()

View file

@ -0,0 +1,16 @@
import macros
template baz() =
proc bar() =
var x = 5
iterator foo(): int {.closure.} =
echo x
var y = foo
discard y()
macro test(): stmt =
result = getAst(baz())
echo(treeRepr(result))
test()
bar()

View file

@ -174,6 +174,10 @@ proc generateJson*(filename: string, commit: int) =
on A.name = B.name and A.category = B.category on A.name = B.name and A.category = B.category
where A.[commit] = ? and B.[commit] = ? and A.machine = ? where A.[commit] = ? and B.[commit] = ? and A.machine = ?
and A.result != B.result""" and A.result != B.result"""
selResults = """select
name, category, target, action, result, expected, given
from TestResult
where [commit] = ?"""
var db = open(connection="testament.db", user="testament", password="", var db = open(connection="testament.db", user="testament", password="",
database="testament") database="testament")
let lastCommit = db.getCommit(commit) let lastCommit = db.getCommit(commit)
@ -189,6 +193,20 @@ proc generateJson*(filename: string, commit: int) =
outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#""" % data) outfile.writeln("""{"total": $#, "passed": $#, "skipped": $#""" % data)
let results = newJArray()
for row in db.rows(sql(selResults), lastCommit):
var obj = newJObject()
obj["name"] = %row[0]
obj["category"] = %row[1]
obj["target"] = %row[2]
obj["action"] = %row[3]
obj["result"] = %row[4]
obj["expected"] = %row[5]
obj["given"] = %row[6]
results.add(obj)
outfile.writeln(""", "results": """)
outfile.write(results.pretty)
if not previousCommit.isNil: if not previousCommit.isNil:
let diff = newJArray() let diff = newJArray()

View file

@ -4,7 +4,17 @@ discard """
3 3
1 1
2 2
3''' 3
1
2
3
1
2
3
aa
bb
aa
bb'''
""" """
const s = @[1,2,3] const s = @[1,2,3]
@ -19,3 +29,27 @@ static:
for e in s: for e in s:
echo e echo e
macro bar(x: static[seq[int]]): stmt =
for e in x:
echo e
bar s
bar(@[1, 2, 3])
type
TData = tuple
letters: seq[string]
numbers: seq[int]
const data: TData = (@["aa", "bb"], @[11, 22])
static:
var m = data
for x in m.letters:
echo x
macro ff(d: static[TData]): stmt =
for x in d.letters:
echo x
ff(data)