mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-31 02:12:11 +00:00
type constraints; tuple lifting
This commit is contained in:
@@ -137,7 +137,11 @@ pragma ::= '{.' optInd (colonExpr [comma])* optPar ('.}' | '}')
|
||||
param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr)
|
||||
paramList ::= ['(' [param (comma param)*] optPar ')'] [':' typeDesc]
|
||||
|
||||
genericParam ::= symbol [':' typeDesc] ['=' expr]
|
||||
genericConstraint ::= 'object' | 'tuple' | 'enum' | 'proc' | 'ref' | 'ptr'
|
||||
| 'var' | 'distinct' | primary
|
||||
genericConstraints ::= genericConstraint ( '|' optInd genericConstraint )*
|
||||
|
||||
genericParam ::= symbol [':' genericConstraints] ['=' expr]
|
||||
genericParams ::= '[' genericParam (comma genericParam)* optPar ']'
|
||||
|
||||
|
||||
|
||||
@@ -586,11 +586,11 @@ proc next*(my: var TXmlParser) =
|
||||
of stateNormal:
|
||||
getTok(my)
|
||||
of stateStart:
|
||||
my.state = stateNormal
|
||||
getTok(my)
|
||||
if my.kind == xmlPI and my.a == "xml":
|
||||
# just skip the first ``<?xml >`` processing instruction
|
||||
getTok(my)
|
||||
my.state = stateNormal
|
||||
of stateAttr:
|
||||
# parse an attribute key-value pair:
|
||||
if my.buf[my.bufpos] == '>':
|
||||
|
||||
@@ -765,7 +765,7 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
|
||||
setLen(x, xl + y.len)
|
||||
for i in 0..high(y): x[xl+i] = y[i]
|
||||
|
||||
proc del* [T](x: var seq[T], i: int) {.noSideEffect.} =
|
||||
proc del*[T](x: var seq[T], i: int) {.noSideEffect.} =
|
||||
## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
|
||||
## This is an O(1) operation.
|
||||
var xl = x.len
|
||||
@@ -1111,15 +1111,6 @@ iterator items*(a: cstring): char {.inline.} =
|
||||
yield a[i]
|
||||
inc(i)
|
||||
|
||||
iterator enumerate*[TContainer, TItem](a: TContainer): tuple[
|
||||
index: int, item: TItem] {.inline.} =
|
||||
## iterates over each item of `a` via `items` and yields an additional
|
||||
## counter/index starting from 0.
|
||||
var j = 0
|
||||
for it in items(a):
|
||||
yield (j, a)
|
||||
inc j
|
||||
|
||||
proc isNil*[T](x: seq[T]): bool {.noSideEffect, magic: "IsNil".}
|
||||
proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".}
|
||||
proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil".}
|
||||
@@ -1170,7 +1161,7 @@ when not defined(NimrodVM):
|
||||
|
||||
proc find*[T, S: typeDesc](a: T, item: S): int {.inline.}=
|
||||
## Returns the first index of `item` in `a` or -1 if not found. This requires
|
||||
## appropriate `items` and `==` procs to work.
|
||||
## appropriate `items` and `==` operations to work.
|
||||
for i in items(a):
|
||||
if i == item: return
|
||||
inc(result)
|
||||
@@ -1200,6 +1191,62 @@ proc each*[T](data: var openArray[T], op: proc (x: var T)) =
|
||||
## `op` to every item in `data`.
|
||||
for i in 0..data.len-1: op(data[i])
|
||||
|
||||
iterator fields*(x: tuple[]): expr {.magic: "Fields", noSideEffect.}
|
||||
## iterates over every field of `x`. Warning: This is really transforms
|
||||
## the 'for' and unrolls the loop. The current implementation also has a bug
|
||||
## that affects symbol binding in the loop body.
|
||||
iterator fields*(x, y: tuple[]): tuple[a, b: expr] {.
|
||||
magic: "Fields", noSideEffect.}
|
||||
## iterates over every field of `x` and `y`.
|
||||
## Warning: This is really transforms the 'for' and unrolls the loop.
|
||||
## The current implementation also has a bug that affects symbol binding
|
||||
## in the loop body.
|
||||
iterator fieldPairs*(x: tuple[]): expr {.magic: "FieldPairs", noSideEffect.}
|
||||
## iterates over every field of `x`. Warning: This is really transforms
|
||||
## the 'for' and unrolls the loop. The current implementation also has a bug
|
||||
## that affects symbol binding in the loop body.
|
||||
iterator fieldPairs*(x, y: tuple[]): tuple[a, b: expr] {.
|
||||
magic: "FieldPairs", noSideEffect.}
|
||||
## iterates over every field of `x` and `y`.
|
||||
## Warning: This is really transforms the 'for' and unrolls the loop.
|
||||
## The current implementation also has a bug that affects symbol binding
|
||||
## in the loop body.
|
||||
|
||||
proc `==`*[T: tuple](x, y: T): bool =
|
||||
## generic ``==`` operator that is lifted from the components
|
||||
## of `x` and `y`.
|
||||
for a, b in fields(x, y):
|
||||
if a != b: return false
|
||||
return true
|
||||
|
||||
proc `<=`*[T: tuple](x, y: T): bool =
|
||||
## generic ``<=`` operator that is lifted from the components
|
||||
## of `x` and `y`. This implementation uses `cmp`.
|
||||
for a, b in fields(x, y):
|
||||
var c = cmp(a, b)
|
||||
if c < 0: return true
|
||||
if c > 0: return false
|
||||
return true
|
||||
|
||||
proc `<`*[T: tuple](x, y: T): bool =
|
||||
## generic ``<`` operator that is lifted from the components
|
||||
## of `x` and `y`. This implementation uses `cmp`.
|
||||
for a, b in fields(x, y):
|
||||
var c = cmp(a, b)
|
||||
if c < 0: return true
|
||||
if c > 0: return false
|
||||
return false
|
||||
|
||||
proc `$`*[T: tuple](x: T): string =
|
||||
## generic ``$`` operator that is lifted from the components of `x`.
|
||||
result = "("
|
||||
for name, value in fieldPairs(x):
|
||||
if result.len > 1: result.add(", ")
|
||||
result.add(name)
|
||||
result.add(": ")
|
||||
result.add($value)
|
||||
result.add(")")
|
||||
|
||||
# ----------------- GC interface ---------------------------------------------
|
||||
|
||||
proc GC_disable*() {.rtl, inl.}
|
||||
|
||||
@@ -237,7 +237,7 @@ type
|
||||
tyGenericParam, # ``a`` in the example
|
||||
tyDistinct,
|
||||
tyEnum,
|
||||
tyOrdinal,
|
||||
tyOrdinal, # misnamed: should become 'tyConstraint'
|
||||
tyArray,
|
||||
tyObject,
|
||||
tyTuple,
|
||||
@@ -329,7 +329,9 @@ type
|
||||
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
|
||||
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
|
||||
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT,
|
||||
mConTArr, mConTT, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mConTArr, mConTT, mSlice,
|
||||
mFields, mFieldPairs,
|
||||
mAppendStrCh, mAppendStrStr, mAppendSeqElem,
|
||||
mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert,
|
||||
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mReset,
|
||||
mArray, mOpenArray, mRange, mSet, mSeq,
|
||||
|
||||
@@ -1014,12 +1014,77 @@ proc parseAsm(p: var TParser): PNode =
|
||||
return
|
||||
getTok(p)
|
||||
|
||||
proc parseGenericConstraint(p: var TParser): PNode =
|
||||
case p.tok.tokType
|
||||
of tkObject:
|
||||
result = newNodeP(nkObjectTy, p)
|
||||
getTok(p)
|
||||
of tkTuple:
|
||||
result = newNodeP(nkTupleTy, p)
|
||||
getTok(p)
|
||||
of tkEnum:
|
||||
result = newNodeP(nkEnumTy, p)
|
||||
getTok(p)
|
||||
of tkProc:
|
||||
result = newNodeP(nkProcTy, p)
|
||||
getTok(p)
|
||||
of tkVar:
|
||||
result = newNodeP(nkVarTy, p)
|
||||
getTok(p)
|
||||
of tkPtr:
|
||||
result = newNodeP(nkPtrTy, p)
|
||||
getTok(p)
|
||||
of tkRef:
|
||||
result = newNodeP(nkRefTy, p)
|
||||
getTok(p)
|
||||
of tkDistinct:
|
||||
result = newNodeP(nkDistinctTy, p)
|
||||
getTok(p)
|
||||
else: result = primary(p)
|
||||
|
||||
proc parseGenericConstraintList(p: var TParser): PNode =
|
||||
result = parseGenericConstraint(p)
|
||||
while p.tok.tokType == tkOpr:
|
||||
var a = result
|
||||
result = newNodeP(nkInfix, p)
|
||||
addSon(result, newIdentNodeP(p.tok.ident, p))
|
||||
addSon(result, a)
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
addSon(result, parseGenericConstraint(p))
|
||||
|
||||
proc parseGenericParam(p: var TParser): PNode =
|
||||
var a: PNode
|
||||
result = newNodeP(nkIdentDefs, p)
|
||||
while true:
|
||||
case p.tok.tokType
|
||||
of tkSymbol, tkAccent:
|
||||
a = parseSymbol(p)
|
||||
if a.kind == nkEmpty: return
|
||||
else: break
|
||||
addSon(result, a)
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
optInd(p, a)
|
||||
if p.tok.tokType == tkColon:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
addSon(result, parseGenericConstraintList(p))
|
||||
else:
|
||||
addSon(result, ast.emptyNode)
|
||||
if p.tok.tokType == tkEquals:
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
addSon(result, parseExpr(p))
|
||||
else:
|
||||
addSon(result, ast.emptyNode)
|
||||
|
||||
proc parseGenericParamList(p: var TParser): PNode =
|
||||
result = newNodeP(nkGenericParams, p)
|
||||
getTok(p)
|
||||
optInd(p, result)
|
||||
while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent):
|
||||
var a = parseIdentColonEquals(p, {withBothOptional})
|
||||
var a = parseGenericParam(p)
|
||||
addSon(result, a)
|
||||
if p.tok.tokType != tkComma: break
|
||||
getTok(p)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#
|
||||
#
|
||||
# The Nimrod Compiler
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
# (c) Copyright 2011 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
@@ -839,17 +839,29 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
putWithSpace(g, tkType, "type")
|
||||
gsub(g, n.sons[0])
|
||||
of nkRefTy:
|
||||
putWithSpace(g, tkRef, "ref")
|
||||
gsub(g, n.sons[0])
|
||||
if sonsLen(n) > 0:
|
||||
putWithSpace(g, tkRef, "ref")
|
||||
gsub(g, n.sons[0])
|
||||
else:
|
||||
put(g, tkRef, "ref")
|
||||
of nkPtrTy:
|
||||
putWithSpace(g, tkPtr, "ptr")
|
||||
gsub(g, n.sons[0])
|
||||
if sonsLen(n) > 0:
|
||||
putWithSpace(g, tkPtr, "ptr")
|
||||
gsub(g, n.sons[0])
|
||||
else:
|
||||
put(g, tkPtr, "ptr")
|
||||
of nkVarTy:
|
||||
putWithSpace(g, tkVar, "var")
|
||||
gsub(g, n.sons[0])
|
||||
if sonsLen(n) > 0:
|
||||
putWithSpace(g, tkVar, "var")
|
||||
gsub(g, n.sons[0])
|
||||
else:
|
||||
put(g, tkVar, "var")
|
||||
of nkDistinctTy:
|
||||
putWithSpace(g, tkDistinct, "distinct")
|
||||
gsub(g, n.sons[0])
|
||||
if sonsLen(n) > 0:
|
||||
putWithSpace(g, tkDistinct, "distinct")
|
||||
gsub(g, n.sons[0])
|
||||
else:
|
||||
put(g, tkDistinct, "distinct")
|
||||
of nkTypeDef:
|
||||
gsub(g, n.sons[0])
|
||||
gsub(g, n.sons[1])
|
||||
@@ -858,11 +870,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
putWithSpace(g, tkEquals, "=")
|
||||
gsub(g, n.sons[2])
|
||||
of nkObjectTy:
|
||||
putWithSpace(g, tkObject, "object")
|
||||
gsub(g, n.sons[0])
|
||||
gsub(g, n.sons[1])
|
||||
gcoms(g)
|
||||
gsub(g, n.sons[2])
|
||||
if sonsLen(n) > 0:
|
||||
putWithSpace(g, tkObject, "object")
|
||||
gsub(g, n.sons[0])
|
||||
gsub(g, n.sons[1])
|
||||
gcoms(g)
|
||||
gsub(g, n.sons[2])
|
||||
else:
|
||||
put(g, tkObject, "object")
|
||||
of nkRecList:
|
||||
indentNL(g)
|
||||
for i in countup(0, sonsLen(n) - 1):
|
||||
@@ -875,17 +890,23 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
putWithSpace(g, tkOf, "of")
|
||||
gsub(g, n.sons[0])
|
||||
of nkProcTy:
|
||||
putWithSpace(g, tkProc, "proc")
|
||||
gsub(g, n.sons[0])
|
||||
gsub(g, n.sons[1])
|
||||
if sonsLen(n) > 0:
|
||||
putWithSpace(g, tkProc, "proc")
|
||||
gsub(g, n.sons[0])
|
||||
gsub(g, n.sons[1])
|
||||
else:
|
||||
put(g, tkProc, "proc")
|
||||
of nkEnumTy:
|
||||
putWithSpace(g, tkEnum, "enum")
|
||||
gsub(g, n.sons[0])
|
||||
gcoms(g)
|
||||
indentNL(g)
|
||||
gcommaAux(g, n, g.indent, 1)
|
||||
gcoms(g) # BUGFIX: comment for the last enum field
|
||||
dedent(g)
|
||||
if sonsLen(n) > 0:
|
||||
putWithSpace(g, tkEnum, "enum")
|
||||
gsub(g, n.sons[0])
|
||||
gcoms(g)
|
||||
indentNL(g)
|
||||
gcommaAux(g, n, g.indent, 1)
|
||||
gcoms(g) # BUGFIX: comment for the last enum field
|
||||
dedent(g)
|
||||
else:
|
||||
put(g, tkEnum, "enum")
|
||||
of nkEnumFieldDef:
|
||||
gsub(g, n.sons[0])
|
||||
put(g, tkSpaces, Space)
|
||||
@@ -1033,9 +1054,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
|
||||
gsub(g, n.sons[0])
|
||||
of nkTupleTy:
|
||||
put(g, tkTuple, "tuple")
|
||||
put(g, tkBracketLe, "[")
|
||||
gcomma(g, n)
|
||||
put(g, tkBracketRi, "]")
|
||||
if sonsLen(n) > 0:
|
||||
put(g, tkBracketLe, "[")
|
||||
gcomma(g, n)
|
||||
put(g, tkBracketRi, "]")
|
||||
else:
|
||||
#nkNone, nkMetaNode, nkTableConstr, nkExplicitTypeListCall:
|
||||
InternalError(n.info, "rnimsyn.gsub(" & $n.kind & ')')
|
||||
|
||||
@@ -118,7 +118,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||
c.p = oldP # restore
|
||||
c.module = oldMod
|
||||
dec(c.InstCounter)
|
||||
|
||||
|
||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
||||
var cl: TReplTypeVars
|
||||
InitIdTable(cl.symMap)
|
||||
|
||||
171
rod/semstmts.nim
171
rod/semstmts.nim
@@ -358,49 +358,120 @@ proc semConst(c: PContext, n: PNode): PNode =
|
||||
addSon(b, copyTree(def))
|
||||
addSon(result, b)
|
||||
|
||||
proc transfFieldLoopBody(n: PNode, forLoop: PNode,
|
||||
tupleType: PType,
|
||||
tupleIndex, first: int): PNode =
|
||||
case n.kind
|
||||
of nkEmpty..pred(nkIdent), succ(nkIdent)..nkNilLit: result = n
|
||||
of nkIdent:
|
||||
result = n
|
||||
var L = sonsLen(forLoop)
|
||||
# field name:
|
||||
if first > 0:
|
||||
if n.ident.id == forLoop[0].ident.id:
|
||||
if tupleType.n == nil:
|
||||
# ugh, there are no field names:
|
||||
result = newStrNode(nkStrLit, "")
|
||||
else:
|
||||
result = newStrNode(nkStrLit, tupleType.n.sons[tupleIndex].sym.name.s)
|
||||
return
|
||||
# other fields:
|
||||
for i in first..L-3:
|
||||
if n.ident.id == forLoop[i].ident.id:
|
||||
var call = forLoop.sons[L-2]
|
||||
var tupl = call.sons[i+1-first]
|
||||
result = newNodeI(nkBracketExpr, n.info)
|
||||
result.add(tupl)
|
||||
result.add(newIntNode(nkIntLit, tupleIndex))
|
||||
break
|
||||
else:
|
||||
result = copyNode(n)
|
||||
newSons(result, sonsLen(n))
|
||||
for i in countup(0, sonsLen(n)-1):
|
||||
result.sons[i] = transfFieldLoopBody(n.sons[i], forLoop,
|
||||
tupleType, tupleIndex, first)
|
||||
|
||||
proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||
# so that 'break' etc. work as expected, we produce
|
||||
# a 'while true: stmt; break' loop ...
|
||||
result = newNodeI(nkWhileStmt, n.info)
|
||||
var trueSymbol = StrTableGet(magicsys.systemModule.Tab, getIdent"true")
|
||||
if trueSymbol == nil: GlobalError(n.info, errSystemNeeds, "true")
|
||||
|
||||
result.add(newSymNode(trueSymbol, n.info))
|
||||
var stmts = newNodeI(nkStmtList, n.info)
|
||||
result.add(stmts)
|
||||
|
||||
var length = sonsLen(n)
|
||||
var call = n.sons[length-2]
|
||||
if length-2 != sonsLen(call)-1 + ord(m==mFieldPairs):
|
||||
GlobalError(n.info, errWrongNumberOfVariables)
|
||||
|
||||
var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar)
|
||||
if tupleTypeA.kind != tyTuple: InternalError(n.info, "no tuple type!")
|
||||
for i in 1..call.len-1:
|
||||
var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar)
|
||||
if not SameType(tupleTypeA, tupleTypeB):
|
||||
typeMismatch(call.sons[i], tupleTypeA, tupleTypeB)
|
||||
|
||||
Inc(c.p.nestedLoopCounter)
|
||||
var loopBody = n.sons[length-1]
|
||||
for i in 0..sonsLen(tupleTypeA)-1:
|
||||
openScope(c.tab)
|
||||
var body = transfFieldLoopBody(loopBody, n, tupleTypeA, i,
|
||||
ord(m==mFieldPairs))
|
||||
stmts.add(SemStmt(c, body))
|
||||
closeScope(c.tab)
|
||||
Dec(c.p.nestedLoopCounter)
|
||||
var b = newNodeI(nkBreakStmt, n.info)
|
||||
b.add(ast.emptyNode)
|
||||
stmts.add(b)
|
||||
|
||||
proc createCountupNode(c: PContext, rangeNode: PNode): PNode =
|
||||
# convert ``in 3..5`` to ``in countup(3, 5)``
|
||||
checkSonsLen(rangeNode, 2)
|
||||
result = newNodeI(nkCall, rangeNode.info)
|
||||
var countUp = StrTableGet(magicsys.systemModule.Tab, getIdent"countup")
|
||||
if countUp == nil: GlobalError(rangeNode.info, errSystemNeeds, "countup")
|
||||
newSons(result, 3)
|
||||
result.sons[0] = newSymNode(countup)
|
||||
result.sons[1] = rangeNode.sons[0]
|
||||
result.sons[2] = rangeNode.sons[1]
|
||||
|
||||
proc semFor(c: PContext, n: PNode): PNode =
|
||||
var
|
||||
v, countup: PSym
|
||||
iter: PType
|
||||
countupNode, call: PNode
|
||||
result = n
|
||||
checkMinSonsLen(n, 3)
|
||||
var length = sonsLen(n)
|
||||
openScope(c.tab)
|
||||
if n.sons[length - 2].kind == nkRange:
|
||||
checkSonsLen(n.sons[length - 2], 2)
|
||||
# convert ``in 3..5`` to ``in countup(3, 5)``
|
||||
countupNode = newNodeI(nkCall, n.sons[length - 2].info)
|
||||
countUp = StrTableGet(magicsys.systemModule.Tab, getIdent("countup"))
|
||||
if countUp == nil: GlobalError(countupNode.info, errSystemNeeds, "countup")
|
||||
newSons(countupNode, 3)
|
||||
countupnode.sons[0] = newSymNode(countup)
|
||||
countupNode.sons[1] = n.sons[length - 2].sons[0]
|
||||
countupNode.sons[2] = n.sons[length - 2].sons[1]
|
||||
n.sons[length - 2] = countupNode
|
||||
n.sons[length - 2] = semExprWithType(c, n.sons[length - 2], {efWantIterator})
|
||||
call = n.sons[length - 2]
|
||||
if (call.kind != nkCall) or (call.sons[0].kind != nkSym) or
|
||||
(call.sons[0].sym.kind != skIterator):
|
||||
if n.sons[length-2].kind == nkRange:
|
||||
n.sons[length-2] = createCountupNode(c, n.sons[length-2])
|
||||
n.sons[length-2] = semExprWithType(c, n.sons[length-2], {efWantIterator})
|
||||
var call = n.sons[length-2]
|
||||
if call.kind != nkCall or call.sons[0].kind != nkSym or
|
||||
call.sons[0].sym.kind != skIterator:
|
||||
GlobalError(n.sons[length - 2].info, errIteratorExpected)
|
||||
iter = skipTypes(n.sons[length - 2].typ, {tyGenericInst})
|
||||
if iter.kind != tyTuple:
|
||||
if length != 3: GlobalError(n.info, errWrongNumberOfVariables)
|
||||
v = newSymS(skForVar, n.sons[0], c)
|
||||
v.typ = iter
|
||||
n.sons[0] = newSymNode(v)
|
||||
addDecl(c, v)
|
||||
else:
|
||||
if length-2 != sonsLen(iter): GlobalError(n.info, errWrongNumberOfVariables)
|
||||
for i in countup(0, length - 3):
|
||||
v = newSymS(skForVar, n.sons[i], c)
|
||||
v.typ = iter.sons[i]
|
||||
n.sons[i] = newSymNode(v)
|
||||
elif call.sons[0].sym.magic != mNone:
|
||||
result = semForFields(c, n, call.sons[0].sym.magic)
|
||||
else:
|
||||
var iter = skipTypes(n.sons[length-2].typ, {tyGenericInst})
|
||||
if iter.kind != tyTuple:
|
||||
if length != 3: GlobalError(n.info, errWrongNumberOfVariables)
|
||||
var v = newSymS(skForVar, n.sons[0], c)
|
||||
v.typ = iter
|
||||
n.sons[0] = newSymNode(v)
|
||||
addDecl(c, v)
|
||||
Inc(c.p.nestedLoopCounter)
|
||||
n.sons[length - 1] = SemStmt(c, n.sons[length - 1])
|
||||
else:
|
||||
if length-2 != sonsLen(iter):
|
||||
GlobalError(n.info, errWrongNumberOfVariables)
|
||||
for i in countup(0, length - 3):
|
||||
var v = newSymS(skForVar, n.sons[i], c)
|
||||
v.typ = iter.sons[i]
|
||||
n.sons[i] = newSymNode(v)
|
||||
addDecl(c, v)
|
||||
Inc(c.p.nestedLoopCounter)
|
||||
n.sons[length-1] = SemStmt(c, n.sons[length-1])
|
||||
Dec(c.p.nestedLoopCounter)
|
||||
closeScope(c.tab)
|
||||
Dec(c.p.nestedLoopCounter)
|
||||
|
||||
proc semRaise(c: PContext, n: PNode): PNode =
|
||||
result = n
|
||||
@@ -436,34 +507,6 @@ proc semTry(c: PContext, n: PNode): PNode =
|
||||
# last child of an nkExcept/nkFinally branch is a statement:
|
||||
a.sons[length - 1] = semStmtScope(c, a.sons[length - 1])
|
||||
|
||||
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
result = copyNode(n)
|
||||
if n.kind != nkGenericParams: InternalError(n.info, "semGenericParamList")
|
||||
for i in countup(0, sonsLen(n)-1):
|
||||
var a = n.sons[i]
|
||||
if a.kind != nkIdentDefs: illFormedAst(n)
|
||||
var L = sonsLen(a)
|
||||
var def = a.sons[L-1]
|
||||
var typ: PType
|
||||
if a.sons[L-2].kind != nkEmpty: typ = semTypeNode(c, a.sons[L-2], nil)
|
||||
elif def.kind != nkEmpty: typ = newTypeS(tyExpr, c)
|
||||
else: typ = nil
|
||||
for j in countup(0, L-3):
|
||||
var s: PSym
|
||||
if (typ == nil) or (typ.kind == tyTypeDesc):
|
||||
s = newSymS(skType, a.sons[j], c)
|
||||
s.typ = newTypeS(tyGenericParam, c)
|
||||
else:
|
||||
# not a type param, but an expression
|
||||
s = newSymS(skGenericParam, a.sons[j], c)
|
||||
s.typ = typ
|
||||
if def.kind != nkEmpty: s.ast = def
|
||||
s.typ.sym = s
|
||||
if father != nil: addSon(father, s.typ)
|
||||
s.position = i
|
||||
addSon(result, newSymNode(s))
|
||||
addDecl(c, s)
|
||||
|
||||
proc addGenericParamListToScope(c: PContext, n: PNode) =
|
||||
if n.kind != nkGenericParams:
|
||||
InternalError(n.info, "addGenericParamListToScope")
|
||||
@@ -741,7 +784,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
|
||||
var t = s.typ
|
||||
if t.sons[0] == nil:
|
||||
LocalError(n.info, errXNeedsReturnType, "iterator")
|
||||
if n.sons[codePos].kind == nkEmpty:
|
||||
if n.sons[codePos].kind == nkEmpty and s.magic == mNone:
|
||||
LocalError(n.info, errImplOfXexpected, s.name.s)
|
||||
|
||||
proc semProc(c: PContext, n: PNode): PNode =
|
||||
|
||||
@@ -173,12 +173,12 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
|
||||
else:
|
||||
GlobalError(n.info, errXExpectsOneTypeParam, "ordinal")
|
||||
|
||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
proc semTypeIdent(c: PContext, n: PNode): PSym =
|
||||
result = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared})
|
||||
if (result != nil):
|
||||
if result != nil:
|
||||
markUsed(n, result)
|
||||
if result.kind != skType: GlobalError(n.info, errTypeExpected)
|
||||
else:
|
||||
else:
|
||||
GlobalError(n.info, errIdentifierExpected)
|
||||
|
||||
proc semTuple(c: PContext, n: PNode, prev: PType): PType =
|
||||
@@ -441,8 +441,6 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
|
||||
addInheritedFields(c, check, pos, concreteBase)
|
||||
else:
|
||||
debug base
|
||||
debug concreteBase
|
||||
localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
|
||||
if n.kind != nkObjectTy: InternalError(n.info, "semObjectNode")
|
||||
result = newOrPrevType(tyObject, prev, c)
|
||||
@@ -632,10 +630,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
|
||||
m.typ.kind = kind
|
||||
m.typ.align = size
|
||||
m.typ.size = size #m.typ.sym := nil;
|
||||
m.typ.size = size
|
||||
|
||||
proc processMagicType(c: PContext, m: PSym) =
|
||||
case m.magic #registerSysType(m.typ);
|
||||
case m.magic
|
||||
of mInt: setMagicType(m, tyInt, intSize)
|
||||
of mInt8: setMagicType(m, tyInt8, 1)
|
||||
of mInt16: setMagicType(m, tyInt16, 2)
|
||||
@@ -656,13 +654,70 @@ proc processMagicType(c: PContext, m: PSym) =
|
||||
of mEmptySet:
|
||||
setMagicType(m, tySet, 1)
|
||||
addSon(m.typ, newTypeS(tyEmpty, c))
|
||||
of mIntSetBaseType:
|
||||
setMagicType(m, tyRange, intSize) #intSetBaseType := m.typ;
|
||||
return
|
||||
of mIntSetBaseType: setMagicType(m, tyRange, intSize)
|
||||
of mNil: setMagicType(m, tyNil, ptrSize)
|
||||
of mExpr: setMagicType(m, tyExpr, 0)
|
||||
of mStmt: setMagicType(m, tyStmt, 0)
|
||||
of mTypeDesc: setMagicType(m, tyTypeDesc, 0)
|
||||
of mArray, mOpenArray, mRange, mSet, mSeq, mOrdinal: return
|
||||
of mArray, mOpenArray, mRange, mSet, mSeq, mOrdinal: nil
|
||||
else: GlobalError(m.info, errTypeExpected)
|
||||
|
||||
proc newConstraint(c: PContext, k: TTypeKind): PType =
|
||||
result = newTypeS(tyOrdinal, c)
|
||||
result.addSon(newTypeS(k, c))
|
||||
|
||||
proc semGenericConstraints(c: PContext, n: PNode, result: PType) =
|
||||
case n.kind
|
||||
of nkProcTy: result.addSon(newConstraint(c, tyProc))
|
||||
of nkEnumTy: result.addSon(newConstraint(c, tyEnum))
|
||||
of nkObjectTy: result.addSon(newConstraint(c, tyObject))
|
||||
of nkTupleTy: result.addSon(newConstraint(c, tyTuple))
|
||||
of nkDistinctTy: result.addSon(newConstraint(c, tyDistinct))
|
||||
of nkVarTy: result.addSon(newConstraint(c, tyVar))
|
||||
of nkPtrTy: result.addSon(newConstraint(c, tyPtr))
|
||||
of nkRefTy: result.addSon(newConstraint(c, tyRef))
|
||||
of nkInfix:
|
||||
semGenericConstraints(c, n.sons[1], result)
|
||||
semGenericConstraints(c, n.sons[2], result)
|
||||
else:
|
||||
result.addSon(semTypeNode(c, n, nil))
|
||||
|
||||
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
|
||||
result = copyNode(n)
|
||||
if n.kind != nkGenericParams: InternalError(n.info, "semGenericParamList")
|
||||
for i in countup(0, sonsLen(n)-1):
|
||||
var a = n.sons[i]
|
||||
if a.kind != nkIdentDefs: illFormedAst(n)
|
||||
var L = sonsLen(a)
|
||||
var def = a.sons[L-1]
|
||||
var typ: PType
|
||||
if a.sons[L-2].kind != nkEmpty:
|
||||
typ = newTypeS(tyGenericParam, c)
|
||||
semGenericConstraints(c, a.sons[L-2], typ)
|
||||
elif def.kind != nkEmpty: typ = newTypeS(tyExpr, c)
|
||||
else: typ = nil
|
||||
for j in countup(0, L-3):
|
||||
var s: PSym
|
||||
if typ == nil:
|
||||
s = newSymS(skType, a.sons[j], c)
|
||||
s.typ = newTypeS(tyGenericParam, c)
|
||||
else:
|
||||
case typ.kind
|
||||
of tyTypeDesc:
|
||||
s = newSymS(skType, a.sons[j], c)
|
||||
s.typ = newTypeS(tyGenericParam, c)
|
||||
of tyExpr:
|
||||
# not a type param, but an expression
|
||||
s = newSymS(skGenericParam, a.sons[j], c)
|
||||
s.typ = typ
|
||||
else:
|
||||
s = newSymS(skType, a.sons[j], c)
|
||||
s.typ = typ
|
||||
if def.kind != nkEmpty: s.ast = def
|
||||
s.typ.sym = s
|
||||
if father != nil: addSon(father, s.typ)
|
||||
s.position = i
|
||||
addSon(result, newSymNode(s))
|
||||
addDecl(c, s)
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ type
|
||||
# for example
|
||||
|
||||
TTypeRelation* = enum # order is important!
|
||||
isNone, isConvertible, isIntConv, isSubtype, isGeneric, isEqual
|
||||
isNone, isConvertible, isIntConv, isSubtype,
|
||||
isLifted, # match, but do not change argument type to formal's type!
|
||||
isGeneric,
|
||||
isEqual
|
||||
|
||||
proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
|
||||
c.exactMatches = 0
|
||||
@@ -144,8 +147,8 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
|
||||
else:
|
||||
var k = skipTypes(a, {tyRange}).kind
|
||||
if k == f.kind: result = isSubtype
|
||||
elif (f.kind == tyInt) and (k in {tyInt..tyInt32}): result = isIntConv
|
||||
elif (k >= min) and (k <= max): result = isConvertible
|
||||
elif f.kind == tyInt and k in {tyInt..tyInt32}: result = isIntConv
|
||||
elif k >= min and k <= max: result = isConvertible
|
||||
else: result = isNone
|
||||
|
||||
proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
@@ -159,7 +162,7 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
|
||||
|
||||
proc isObjectSubtype(a, f: PType): bool =
|
||||
var t = a
|
||||
while (t != nil) and (t.id != f.id): t = base(t)
|
||||
while t != nil and t.id != f.id: t = base(t)
|
||||
result = t != nil
|
||||
|
||||
proc minRel(a, b: TTypeRelation): TTypeRelation =
|
||||
@@ -182,11 +185,15 @@ proc tupleRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
var x = f.n.sons[i].sym
|
||||
var y = a.n.sons[i].sym
|
||||
if x.name.id != y.name.id: return isNone
|
||||
elif sonsLen(f) == 0:
|
||||
idTablePut(mapping, f, a)
|
||||
result = isLifted
|
||||
|
||||
proc constraintRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
result = isNone
|
||||
if f.kind == a.kind: result = isGeneric
|
||||
|
||||
proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
var
|
||||
x, concrete: PType
|
||||
m: TTypeRelation
|
||||
# is a subtype of f?
|
||||
result = isNone
|
||||
assert(f != nil)
|
||||
@@ -272,9 +279,11 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
if result < isGeneric: result = isNone
|
||||
else: nil
|
||||
of tyOrdinal:
|
||||
if isOrdinalType(a):
|
||||
if a.kind == tyOrdinal: x = a.sons[0]
|
||||
else: x = a
|
||||
if f.sons[0].kind != tyGenericParam:
|
||||
# some constraint:
|
||||
result = constraintRel(mapping, f.sons[0], a)
|
||||
elif isOrdinalType(a):
|
||||
var x = if a.kind == tyOrdinal: a.sons[0] else: a
|
||||
result = typeRel(mapping, f.sons[0], x)
|
||||
if result < isGeneric: result = isNone
|
||||
of tyForward: InternalError("forward type in typeRel()")
|
||||
@@ -319,6 +328,7 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
# return type!
|
||||
result = isEqual # start with maximum; also correct for no
|
||||
# params at all
|
||||
var m: TTypeRelation
|
||||
for i in countup(1, sonsLen(f) - 1):
|
||||
m = typeRel(mapping, f.sons[i], a.sons[i])
|
||||
if (m == isNone) and
|
||||
@@ -392,26 +402,25 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
|
||||
if result != isNone:
|
||||
# we steal the generic parameters from the tyGenericBody:
|
||||
for i in countup(1, sonsLen(f) - 1):
|
||||
x = PType(idTableGet(mapping, f.sons[0].sons[i - 1]))
|
||||
var x = PType(idTableGet(mapping, f.sons[0].sons[i - 1]))
|
||||
if (x == nil) or (x.kind == tyGenericParam):
|
||||
InternalError("wrong instantiated type!")
|
||||
idTablePut(mapping, f.sons[i], x)
|
||||
of tyGenericParam:
|
||||
x = PType(idTableGet(mapping, f))
|
||||
var x = PType(idTableGet(mapping, f))
|
||||
if x == nil:
|
||||
if sonsLen(f) == 0:
|
||||
# no constraints
|
||||
concrete = concreteType(mapping, a)
|
||||
var concrete = concreteType(mapping, a)
|
||||
if concrete != nil:
|
||||
#MessageOut('putting: ' + f.sym.name.s);
|
||||
idTablePut(mapping, f, concrete)
|
||||
result = isGeneric
|
||||
else:
|
||||
InternalError(f.sym.info, "has constraints: " & f.sym.name.s)
|
||||
# check constraints:
|
||||
for i in countup(0, sonsLen(f) - 1):
|
||||
if typeRel(mapping, f.sons[i], a) >= isSubtype:
|
||||
concrete = concreteType(mapping, a)
|
||||
var concrete = concreteType(mapping, a)
|
||||
if concrete != nil:
|
||||
idTablePut(mapping, f, concrete)
|
||||
result = isGeneric
|
||||
@@ -484,6 +493,9 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
|
||||
of isSubtype:
|
||||
inc(m.subtypeMatches)
|
||||
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
|
||||
of isLifted:
|
||||
inc(m.genericMatches)
|
||||
result = copyTree(arg)
|
||||
of isGeneric:
|
||||
inc(m.genericMatches)
|
||||
result = copyTree(arg)
|
||||
|
||||
15
tests/accept/compile/tconstraints.nim
Normal file
15
tests/accept/compile/tconstraints.nim
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
proc myGenericProc[T: object|tuple|int|ptr|ref|distinct](x: T): string =
|
||||
result = $x
|
||||
|
||||
type
|
||||
TMyObj = tuple[x, y: int]
|
||||
|
||||
var
|
||||
x: TMyObj
|
||||
|
||||
assert myGenericProc(232) == "232"
|
||||
assert myGenericProc(x) == "(x: 0, y: 0)"
|
||||
|
||||
|
||||
19
tests/accept/compile/tgenericmatcher.nim
Normal file
19
tests/accept/compile/tgenericmatcher.nim
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
type
|
||||
TMatcherKind = enum
|
||||
mkTerminal, mkSequence, mkAlternation, mkRepeat
|
||||
TMatcher[T] = object
|
||||
case kind: TMatcherKind
|
||||
of mkTerminal:
|
||||
value: T
|
||||
of mkSequence, mkAlternation:
|
||||
matchers: seq[TMatcher[T]]
|
||||
of mkRepeat:
|
||||
matcher: PMatcher[T]
|
||||
min, max: int
|
||||
PMatcher[T] = ref TMatcher[T]
|
||||
|
||||
var
|
||||
m: PMatcher[int]
|
||||
|
||||
|
||||
24
tests/accept/compile/tgenericrefs.nim
Normal file
24
tests/accept/compile/tgenericrefs.nim
Normal file
@@ -0,0 +1,24 @@
|
||||
# Compiles:
|
||||
|
||||
type
|
||||
TA[T] = object
|
||||
PA[T] = ref TA[T]
|
||||
var a: PA[string]
|
||||
|
||||
# Compiles unless you use var a: PA[string]
|
||||
type
|
||||
PA = ref TA
|
||||
TA[T] = object
|
||||
|
||||
|
||||
# Cannot instanciate:
|
||||
type
|
||||
TA[T] = object
|
||||
a: PA[T]
|
||||
PA[T] = ref TA[T]
|
||||
|
||||
type
|
||||
PA[T] = ref TA[T]
|
||||
TA[T] = object
|
||||
|
||||
|
||||
46
tests/accept/run/tfielditerator.nim
Normal file
46
tests/accept/run/tfielditerator.nim
Normal file
@@ -0,0 +1,46 @@
|
||||
discard """
|
||||
output: '''
|
||||
a char: true
|
||||
a char: false
|
||||
an int: 5
|
||||
an int: 6
|
||||
a string: abc
|
||||
false
|
||||
true
|
||||
true
|
||||
false
|
||||
true
|
||||
a: a
|
||||
b: b
|
||||
x: 5
|
||||
y: 6
|
||||
z: abc
|
||||
'''
|
||||
"""
|
||||
|
||||
type
|
||||
TMyTuple = tuple[a, b: char, x, y: int, z: string]
|
||||
|
||||
proc p(x: char) = echo "a char: ", x <= 'a'
|
||||
proc p(x: int) = echo "an int: ", x
|
||||
proc p(x: string) = echo "a string: ", x
|
||||
|
||||
var x: TMyTuple = ('a', 'b', 5, 6, "abc")
|
||||
var y: TMyTuple = ('A', 'b', 5, 9, "abc")
|
||||
|
||||
for f in fields(x):
|
||||
p f
|
||||
|
||||
for a, b in fields(x, y):
|
||||
echo a == b
|
||||
|
||||
for key, val in fieldPairs(x):
|
||||
echo key, ": ", val
|
||||
|
||||
assert x != y
|
||||
assert x == x
|
||||
assert(not (x < x))
|
||||
assert x <= x
|
||||
assert y < x
|
||||
assert y <= x
|
||||
|
||||
17
tests/accept/run/tkoeniglookup.nim
Normal file
17
tests/accept/run/tkoeniglookup.nim
Normal file
@@ -0,0 +1,17 @@
|
||||
discard """
|
||||
output: '''x: 0 y: 0'''
|
||||
"""
|
||||
|
||||
proc ToString[T]*(x: T): string = return $x
|
||||
|
||||
|
||||
type
|
||||
TMyObj = object
|
||||
x, y: int
|
||||
|
||||
proc `$`*(a: TMyObj): bool =
|
||||
result = "x: " & a.x & " y: " & a.y
|
||||
|
||||
var a: TMyObj
|
||||
echo toString(a)
|
||||
|
||||
18
tests/reject/tconstraints.nim
Normal file
18
tests/reject/tconstraints.nim
Normal file
@@ -0,0 +1,18 @@
|
||||
discard """
|
||||
line: 15
|
||||
errormsg: "type mismatch: got (int)"
|
||||
"""
|
||||
|
||||
proc myGenericProc[T: object|tuple|ptr|ref|distinct](x: T): string =
|
||||
result = $x
|
||||
|
||||
type
|
||||
TMyObj = tuple[x, y: int]
|
||||
|
||||
var
|
||||
x: TMyObj
|
||||
|
||||
assert myGenericProc(232) == "232"
|
||||
assert myGenericProc(x) == "(x: 0, y: 0)"
|
||||
|
||||
|
||||
9
todo.txt
9
todo.txt
@@ -1,13 +1,8 @@
|
||||
- 'nimrod def': does not always work
|
||||
|
||||
- thread support: threadvar on Windows seems broken;
|
||||
add --deadlock_prevention:on|off switch
|
||||
- built-in serialization
|
||||
|
||||
- deprecate ^ and make it available as operator
|
||||
- test branch coverage
|
||||
- checked exceptions
|
||||
- slicing
|
||||
|
||||
|
||||
High priority (version 0.9.0)
|
||||
@@ -61,6 +56,10 @@ Low priority
|
||||
- nested tuple unpacking; no auto-unpacking in 'for' loops!
|
||||
- better error messages for used keywords as identifiers
|
||||
- case statement branches should support constant sets
|
||||
- 'nimrod def': does not always work
|
||||
- test branch coverage
|
||||
- checked exceptions
|
||||
- slicing
|
||||
|
||||
|
||||
Library
|
||||
|
||||
Reference in New Issue
Block a user