type constraints; tuple lifting

This commit is contained in:
Araq 2011-04-01 00:26:07 +02:00
commit 4d91c9d887
17 changed files with 527 additions and 139 deletions

View file

@ -137,7 +137,11 @@ pragma ::= '{.' optInd (colonExpr [comma])* optPar ('.}' | '}')
param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr) param ::= symbol (comma symbol)* (':' typeDesc ['=' expr] | '=' expr)
paramList ::= ['(' [param (comma param)*] optPar ')'] [':' typeDesc] 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 ']' genericParams ::= '[' genericParam (comma genericParam)* optPar ']'

View file

@ -586,11 +586,11 @@ proc next*(my: var TXmlParser) =
of stateNormal: of stateNormal:
getTok(my) getTok(my)
of stateStart: of stateStart:
my.state = stateNormal
getTok(my) getTok(my)
if my.kind == xmlPI and my.a == "xml": if my.kind == xmlPI and my.a == "xml":
# just skip the first ``<?xml >`` processing instruction # just skip the first ``<?xml >`` processing instruction
getTok(my) getTok(my)
my.state = stateNormal
of stateAttr: of stateAttr:
# parse an attribute key-value pair: # parse an attribute key-value pair:
if my.buf[my.bufpos] == '>': if my.buf[my.bufpos] == '>':

View file

@ -765,7 +765,7 @@ proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} =
setLen(x, xl + y.len) setLen(x, xl + y.len)
for i in 0..high(y): x[xl+i] = y[i] 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`. ## deletes the item at index `i` by putting ``x[high(x)]`` into position `i`.
## This is an O(1) operation. ## This is an O(1) operation.
var xl = x.len var xl = x.len
@ -1111,15 +1111,6 @@ iterator items*(a: cstring): char {.inline.} =
yield a[i] yield a[i]
inc(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: seq[T]): bool {.noSideEffect, magic: "IsNil".}
proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".} proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".}
proc isNil*(x: string): 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.}= 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 ## 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): for i in items(a):
if i == item: return if i == item: return
inc(result) inc(result)
@ -1200,6 +1191,62 @@ proc each*[T](data: var openArray[T], op: proc (x: var T)) =
## `op` to every item in `data`. ## `op` to every item in `data`.
for i in 0..data.len-1: op(data[i]) 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 --------------------------------------------- # ----------------- GC interface ---------------------------------------------
proc GC_disable*() {.rtl, inl.} proc GC_disable*() {.rtl, inl.}

View file

@ -237,7 +237,7 @@ type
tyGenericParam, # ``a`` in the example tyGenericParam, # ``a`` in the example
tyDistinct, tyDistinct,
tyEnum, tyEnum,
tyOrdinal, tyOrdinal, # misnamed: should become 'tyConstraint'
tyArray, tyArray,
tyObject, tyObject,
tyTuple, tyTuple,
@ -329,7 +329,9 @@ type
mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr,
mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet, mAnd, mOr, mEqStr, mLeStr, mLtStr, mEqSet, mLeSet, mLtSet, mMulSet,
mPlusSet, mMinusSet, mSymDiffSet, mConStrStr, mConArrArr, mConArrT, 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, mInRange, mInSet, mRepr, mExit, mSetLengthStr, mSetLengthSeq, mAssert,
mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mReset, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mReset,
mArray, mOpenArray, mRange, mSet, mSeq, mArray, mOpenArray, mRange, mSet, mSeq,

View file

@ -1014,12 +1014,77 @@ proc parseAsm(p: var TParser): PNode =
return return
getTok(p) 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 = proc parseGenericParamList(p: var TParser): PNode =
result = newNodeP(nkGenericParams, p) result = newNodeP(nkGenericParams, p)
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent): while (p.tok.tokType == tkSymbol) or (p.tok.tokType == tkAccent):
var a = parseIdentColonEquals(p, {withBothOptional}) var a = parseGenericParam(p)
addSon(result, a) addSon(result, a)
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
getTok(p) getTok(p)

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -839,17 +839,29 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
putWithSpace(g, tkType, "type") putWithSpace(g, tkType, "type")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
of nkRefTy: of nkRefTy:
if sonsLen(n) > 0:
putWithSpace(g, tkRef, "ref") putWithSpace(g, tkRef, "ref")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
else:
put(g, tkRef, "ref")
of nkPtrTy: of nkPtrTy:
if sonsLen(n) > 0:
putWithSpace(g, tkPtr, "ptr") putWithSpace(g, tkPtr, "ptr")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
else:
put(g, tkPtr, "ptr")
of nkVarTy: of nkVarTy:
if sonsLen(n) > 0:
putWithSpace(g, tkVar, "var") putWithSpace(g, tkVar, "var")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
else:
put(g, tkVar, "var")
of nkDistinctTy: of nkDistinctTy:
if sonsLen(n) > 0:
putWithSpace(g, tkDistinct, "distinct") putWithSpace(g, tkDistinct, "distinct")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
else:
put(g, tkDistinct, "distinct")
of nkTypeDef: of nkTypeDef:
gsub(g, n.sons[0]) gsub(g, n.sons[0])
gsub(g, n.sons[1]) gsub(g, n.sons[1])
@ -858,11 +870,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
putWithSpace(g, tkEquals, "=") putWithSpace(g, tkEquals, "=")
gsub(g, n.sons[2]) gsub(g, n.sons[2])
of nkObjectTy: of nkObjectTy:
if sonsLen(n) > 0:
putWithSpace(g, tkObject, "object") putWithSpace(g, tkObject, "object")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
gsub(g, n.sons[1]) gsub(g, n.sons[1])
gcoms(g) gcoms(g)
gsub(g, n.sons[2]) gsub(g, n.sons[2])
else:
put(g, tkObject, "object")
of nkRecList: of nkRecList:
indentNL(g) indentNL(g)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
@ -875,10 +890,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
putWithSpace(g, tkOf, "of") putWithSpace(g, tkOf, "of")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
of nkProcTy: of nkProcTy:
if sonsLen(n) > 0:
putWithSpace(g, tkProc, "proc") putWithSpace(g, tkProc, "proc")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
gsub(g, n.sons[1]) gsub(g, n.sons[1])
else:
put(g, tkProc, "proc")
of nkEnumTy: of nkEnumTy:
if sonsLen(n) > 0:
putWithSpace(g, tkEnum, "enum") putWithSpace(g, tkEnum, "enum")
gsub(g, n.sons[0]) gsub(g, n.sons[0])
gcoms(g) gcoms(g)
@ -886,6 +905,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gcommaAux(g, n, g.indent, 1) gcommaAux(g, n, g.indent, 1)
gcoms(g) # BUGFIX: comment for the last enum field gcoms(g) # BUGFIX: comment for the last enum field
dedent(g) dedent(g)
else:
put(g, tkEnum, "enum")
of nkEnumFieldDef: of nkEnumFieldDef:
gsub(g, n.sons[0]) gsub(g, n.sons[0])
put(g, tkSpaces, Space) put(g, tkSpaces, Space)
@ -1033,6 +1054,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) =
gsub(g, n.sons[0]) gsub(g, n.sons[0])
of nkTupleTy: of nkTupleTy:
put(g, tkTuple, "tuple") put(g, tkTuple, "tuple")
if sonsLen(n) > 0:
put(g, tkBracketLe, "[") put(g, tkBracketLe, "[")
gcomma(g, n) gcomma(g, n)
put(g, tkBracketRi, "]") put(g, tkBracketRi, "]")

View file

@ -358,49 +358,120 @@ proc semConst(c: PContext, n: PNode): PNode =
addSon(b, copyTree(def)) addSon(b, copyTree(def))
addSon(result, b) 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 = proc semFor(c: PContext, n: PNode): PNode =
var
v, countup: PSym
iter: PType
countupNode, call: PNode
result = n result = n
checkMinSonsLen(n, 3) checkMinSonsLen(n, 3)
var length = sonsLen(n) var length = sonsLen(n)
openScope(c.tab) openScope(c.tab)
if n.sons[length - 2].kind == nkRange: if n.sons[length-2].kind == nkRange:
checkSonsLen(n.sons[length - 2], 2) n.sons[length-2] = createCountupNode(c, n.sons[length-2])
# convert ``in 3..5`` to ``in countup(3, 5)`` n.sons[length-2] = semExprWithType(c, n.sons[length-2], {efWantIterator})
countupNode = newNodeI(nkCall, n.sons[length - 2].info) var call = n.sons[length-2]
countUp = StrTableGet(magicsys.systemModule.Tab, getIdent("countup")) if call.kind != nkCall or call.sons[0].kind != nkSym or
if countUp == nil: GlobalError(countupNode.info, errSystemNeeds, "countup") call.sons[0].sym.kind != skIterator:
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):
GlobalError(n.sons[length - 2].info, errIteratorExpected) GlobalError(n.sons[length - 2].info, errIteratorExpected)
iter = skipTypes(n.sons[length - 2].typ, {tyGenericInst}) 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 iter.kind != tyTuple:
if length != 3: GlobalError(n.info, errWrongNumberOfVariables) if length != 3: GlobalError(n.info, errWrongNumberOfVariables)
v = newSymS(skForVar, n.sons[0], c) var v = newSymS(skForVar, n.sons[0], c)
v.typ = iter v.typ = iter
n.sons[0] = newSymNode(v) n.sons[0] = newSymNode(v)
addDecl(c, v) addDecl(c, v)
else: else:
if length-2 != sonsLen(iter): GlobalError(n.info, errWrongNumberOfVariables) if length-2 != sonsLen(iter):
GlobalError(n.info, errWrongNumberOfVariables)
for i in countup(0, length - 3): for i in countup(0, length - 3):
v = newSymS(skForVar, n.sons[i], c) var v = newSymS(skForVar, n.sons[i], c)
v.typ = iter.sons[i] v.typ = iter.sons[i]
n.sons[i] = newSymNode(v) n.sons[i] = newSymNode(v)
addDecl(c, v) addDecl(c, v)
Inc(c.p.nestedLoopCounter) Inc(c.p.nestedLoopCounter)
n.sons[length - 1] = SemStmt(c, n.sons[length - 1]) n.sons[length-1] = SemStmt(c, n.sons[length-1])
closeScope(c.tab)
Dec(c.p.nestedLoopCounter) Dec(c.p.nestedLoopCounter)
closeScope(c.tab)
proc semRaise(c: PContext, n: PNode): PNode = proc semRaise(c: PContext, n: PNode): PNode =
result = n result = n
@ -436,34 +507,6 @@ proc semTry(c: PContext, n: PNode): PNode =
# last child of an nkExcept/nkFinally branch is a statement: # last child of an nkExcept/nkFinally branch is a statement:
a.sons[length - 1] = semStmtScope(c, a.sons[length - 1]) 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) = proc addGenericParamListToScope(c: PContext, n: PNode) =
if n.kind != nkGenericParams: if n.kind != nkGenericParams:
InternalError(n.info, "addGenericParamListToScope") InternalError(n.info, "addGenericParamListToScope")
@ -741,7 +784,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
var t = s.typ var t = s.typ
if t.sons[0] == nil: if t.sons[0] == nil:
LocalError(n.info, errXNeedsReturnType, "iterator") 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) LocalError(n.info, errImplOfXexpected, s.name.s)
proc semProc(c: PContext, n: PNode): PNode = proc semProc(c: PContext, n: PNode): PNode =

View file

@ -175,7 +175,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
proc semTypeIdent(c: PContext, n: PNode): PSym = proc semTypeIdent(c: PContext, n: PNode): PSym =
result = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared}) result = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared})
if (result != nil): if result != nil:
markUsed(n, result) markUsed(n, result)
if result.kind != skType: GlobalError(n.info, errTypeExpected) if result.kind != skType: GlobalError(n.info, errTypeExpected)
else: else:
@ -441,8 +441,6 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags: if concreteBase.kind == tyObject and tfFinal notin concreteBase.flags:
addInheritedFields(c, check, pos, concreteBase) addInheritedFields(c, check, pos, concreteBase)
else: else:
debug base
debug concreteBase
localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects)
if n.kind != nkObjectTy: InternalError(n.info, "semObjectNode") if n.kind != nkObjectTy: InternalError(n.info, "semObjectNode")
result = newOrPrevType(tyObject, prev, c) 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) = proc setMagicType(m: PSym, kind: TTypeKind, size: int) =
m.typ.kind = kind m.typ.kind = kind
m.typ.align = size m.typ.align = size
m.typ.size = size #m.typ.sym := nil; m.typ.size = size
proc processMagicType(c: PContext, m: PSym) = proc processMagicType(c: PContext, m: PSym) =
case m.magic #registerSysType(m.typ); case m.magic
of mInt: setMagicType(m, tyInt, intSize) of mInt: setMagicType(m, tyInt, intSize)
of mInt8: setMagicType(m, tyInt8, 1) of mInt8: setMagicType(m, tyInt8, 1)
of mInt16: setMagicType(m, tyInt16, 2) of mInt16: setMagicType(m, tyInt16, 2)
@ -656,13 +654,70 @@ proc processMagicType(c: PContext, m: PSym) =
of mEmptySet: of mEmptySet:
setMagicType(m, tySet, 1) setMagicType(m, tySet, 1)
addSon(m.typ, newTypeS(tyEmpty, c)) addSon(m.typ, newTypeS(tyEmpty, c))
of mIntSetBaseType: of mIntSetBaseType: setMagicType(m, tyRange, intSize)
setMagicType(m, tyRange, intSize) #intSetBaseType := m.typ;
return
of mNil: setMagicType(m, tyNil, ptrSize) of mNil: setMagicType(m, tyNil, ptrSize)
of mExpr: setMagicType(m, tyExpr, 0) of mExpr: setMagicType(m, tyExpr, 0)
of mStmt: setMagicType(m, tyStmt, 0) of mStmt: setMagicType(m, tyStmt, 0)
of mTypeDesc: setMagicType(m, tyTypeDesc, 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) 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)

View file

@ -32,7 +32,10 @@ type
# for example # for example
TTypeRelation* = enum # order is important! 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.} = proc initCandidateAux(c: var TCandidate, callee: PType) {.inline.} =
c.exactMatches = 0 c.exactMatches = 0
@ -144,8 +147,8 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
else: else:
var k = skipTypes(a, {tyRange}).kind var k = skipTypes(a, {tyRange}).kind
if k == f.kind: result = isSubtype if k == f.kind: result = isSubtype
elif (f.kind == tyInt) and (k in {tyInt..tyInt32}): result = isIntConv elif f.kind == tyInt and k in {tyInt..tyInt32}: result = isIntConv
elif (k >= min) and (k <= max): result = isConvertible elif k >= min and k <= max: result = isConvertible
else: result = isNone else: result = isNone
proc handleFloatRange(f, a: PType): TTypeRelation = proc handleFloatRange(f, a: PType): TTypeRelation =
@ -159,7 +162,7 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
proc isObjectSubtype(a, f: PType): bool = proc isObjectSubtype(a, f: PType): bool =
var t = a 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 result = t != nil
proc minRel(a, b: TTypeRelation): TTypeRelation = 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 x = f.n.sons[i].sym
var y = a.n.sons[i].sym var y = a.n.sons[i].sym
if x.name.id != y.name.id: return isNone 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 = proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
var
x, concrete: PType
m: TTypeRelation
# is a subtype of f? # is a subtype of f?
result = isNone result = isNone
assert(f != nil) assert(f != nil)
@ -272,9 +279,11 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
if result < isGeneric: result = isNone if result < isGeneric: result = isNone
else: nil else: nil
of tyOrdinal: of tyOrdinal:
if isOrdinalType(a): if f.sons[0].kind != tyGenericParam:
if a.kind == tyOrdinal: x = a.sons[0] # some constraint:
else: x = a 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) result = typeRel(mapping, f.sons[0], x)
if result < isGeneric: result = isNone if result < isGeneric: result = isNone
of tyForward: InternalError("forward type in typeRel()") of tyForward: InternalError("forward type in typeRel()")
@ -319,6 +328,7 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
# return type! # return type!
result = isEqual # start with maximum; also correct for no result = isEqual # start with maximum; also correct for no
# params at all # params at all
var m: TTypeRelation
for i in countup(1, sonsLen(f) - 1): for i in countup(1, sonsLen(f) - 1):
m = typeRel(mapping, f.sons[i], a.sons[i]) m = typeRel(mapping, f.sons[i], a.sons[i])
if (m == isNone) and if (m == isNone) and
@ -392,26 +402,25 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
if result != isNone: if result != isNone:
# we steal the generic parameters from the tyGenericBody: # we steal the generic parameters from the tyGenericBody:
for i in countup(1, sonsLen(f) - 1): 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): if (x == nil) or (x.kind == tyGenericParam):
InternalError("wrong instantiated type!") InternalError("wrong instantiated type!")
idTablePut(mapping, f.sons[i], x) idTablePut(mapping, f.sons[i], x)
of tyGenericParam: of tyGenericParam:
x = PType(idTableGet(mapping, f)) var x = PType(idTableGet(mapping, f))
if x == nil: if x == nil:
if sonsLen(f) == 0: if sonsLen(f) == 0:
# no constraints # no constraints
concrete = concreteType(mapping, a) var concrete = concreteType(mapping, a)
if concrete != nil: if concrete != nil:
#MessageOut('putting: ' + f.sym.name.s); #MessageOut('putting: ' + f.sym.name.s);
idTablePut(mapping, f, concrete) idTablePut(mapping, f, concrete)
result = isGeneric result = isGeneric
else: else:
InternalError(f.sym.info, "has constraints: " & f.sym.name.s)
# check constraints: # check constraints:
for i in countup(0, sonsLen(f) - 1): for i in countup(0, sonsLen(f) - 1):
if typeRel(mapping, f.sons[i], a) >= isSubtype: if typeRel(mapping, f.sons[i], a) >= isSubtype:
concrete = concreteType(mapping, a) var concrete = concreteType(mapping, a)
if concrete != nil: if concrete != nil:
idTablePut(mapping, f, concrete) idTablePut(mapping, f, concrete)
result = isGeneric result = isGeneric
@ -484,6 +493,9 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
of isSubtype: of isSubtype:
inc(m.subtypeMatches) inc(m.subtypeMatches)
result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c) result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
of isLifted:
inc(m.genericMatches)
result = copyTree(arg)
of isGeneric: of isGeneric:
inc(m.genericMatches) inc(m.genericMatches)
result = copyTree(arg) result = copyTree(arg)

View 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)"

View 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]

View 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

View 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

View 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)

View 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)"

View file

@ -1,13 +1,8 @@
- 'nimrod def': does not always work
- thread support: threadvar on Windows seems broken; - thread support: threadvar on Windows seems broken;
add --deadlock_prevention:on|off switch add --deadlock_prevention:on|off switch
- built-in serialization - built-in serialization
- deprecate ^ and make it available as operator - deprecate ^ and make it available as operator
- test branch coverage
- checked exceptions
- slicing
High priority (version 0.9.0) High priority (version 0.9.0)
@ -61,6 +56,10 @@ Low priority
- nested tuple unpacking; no auto-unpacking in 'for' loops! - nested tuple unpacking; no auto-unpacking in 'for' loops!
- better error messages for used keywords as identifiers - better error messages for used keywords as identifiers
- case statement branches should support constant sets - case statement branches should support constant sets
- 'nimrod def': does not always work
- test branch coverage
- checked exceptions
- slicing
Library Library