simple unit test and better documentation for the user defined type classes

This commit is contained in:
Zahary Karadjov 2013-11-17 22:50:26 +02:00
commit a068aaed3c
8 changed files with 113 additions and 57 deletions

View file

@ -436,7 +436,14 @@ type
# only 8 bytes.
line*, col*: int16
fileIndex*: int32
TErrorOutput* = enum
eStdOut
eStdErr
eInMemory
TErrorOutputs* = set[TErrorOutput]
ERecoverableError* = object of EInvalidValue
ESuggestDone* = object of EBase
@ -534,13 +541,27 @@ var
gHintCounter*: int = 0
gWarnCounter*: int = 0
gErrorMax*: int = 1 # stop after gErrorMax errors
gSilence*: int # == 0 if we produce any output at all
when useCaas:
var stdoutSocket*: TSocket
proc UnknownLineInfo*(): TLineInfo =
result.line = int16(-1)
result.col = int16(-1)
result.fileIndex = -1
var
msgContext: seq[TLineInfo] = @[]
lastError = UnknownLineInfo()
bufferedMsgs*: seq[string]
errorOutputs* = {eStdOut, eStdErr}
proc clearBufferedMsgs* =
bufferedMsgs = nil
proc SuggestWriteln*(s: string) =
if gSilence == 0:
if eStdOut in errorOutputs:
when useCaas:
if isNil(stdoutSocket): Writeln(stdout, s)
else:
@ -548,6 +569,9 @@ proc SuggestWriteln*(s: string) =
stdoutSocket.send(s & "\c\L")
else:
Writeln(stdout, s)
if eInMemory in errorOutputs:
bufferedMsgs.safeAdd(s)
proc SuggestQuit*() =
if not isServing:
@ -570,14 +594,6 @@ const
RawWarningFormat* = "Warning: $1"
RawHintFormat* = "Hint: $1"
proc UnknownLineInfo*(): TLineInfo =
result.line = int16(-1)
result.col = int16(-1)
result.fileIndex = -1
var
msgContext: seq[TLineInfo] = @[]
proc getInfoContextLen*(): int = return msgContext.len
proc setInfoContextLen*(L: int) = setLen(msgContext, L)
@ -642,14 +658,18 @@ proc addCheckpoint*(filename: string, line: int) =
proc OutWriteln*(s: string) =
## Writes to stdout. Always.
if gSilence == 0: Writeln(stdout, s)
if eStdOut in errorOutputs: Writeln(stdout, s)
proc MsgWriteln*(s: string) =
## Writes to stdout. If --stdout option is given, writes to stderr instead.
if gSilence == 0:
if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if optStdout in gGlobalOptions: Writeln(stderr, s)
else: Writeln(stdout, s)
if gCmd == cmdIdeTools and optCDebug notin gGlobalOptions: return
if optStdout in gGlobalOptions:
if eStdErr in errorOutputs: Writeln(stderr, s)
else:
if eStdOut in errorOutputs: Writeln(stdout, s)
if eInMemory in errorOutputs: bufferedMsgs.safeAdd(s)
proc coordToStr(coord: int): string =
if coord == -1: result = "???"
@ -736,9 +756,6 @@ proc rawMessage*(msg: TMsgKind, args: openarray[string]) =
proc rawMessage*(msg: TMsgKind, arg: string) =
rawMessage(msg, [arg])
var
lastError = UnknownLineInfo()
proc writeSurroundingSrc(info: TLineInfo) =
const indent = " "
MsgWriteln(indent & info.sourceLine.ropeToStr)

View file

@ -36,7 +36,8 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
proc addParams(c: PContext, n: PNode, kind: TSymKind)
proc maybeAddResult(c: PContext, s: PSym, n: PNode)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
proc tryExpr(c: PContext, n: PNode,
flags: TExprFlags = {}, bufferErrors = false): PNode
proc fixImmediateParams(n: PNode): PNode
proc activate(c: PContext, n: PNode)
proc semQuoteAst(c: PContext, n: PNode): PNode

View file

@ -73,7 +73,8 @@ type
libs*: TLinkedList # all libs used by this module
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {},
bufferErrors = false): PNode {.nimcall.}
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,

View file

@ -824,7 +824,7 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode =
proc semExprNoType(c: PContext, n: PNode): PNode =
result = semExpr(c, n, {efWantStmt})
discardCheck(result)
discardCheck(c, result)
proc isTypeExpr(n: PNode): bool =
case n.kind
@ -1218,7 +1218,7 @@ proc semProcBody(c: PContext, n: PNode): PNode =
a.sons[1] = result
result = semAsgn(c, a)
else:
discardCheck(result)
discardCheck(c, result)
closeScope(c)
proc SemYieldVarResult(c: PContext, n: PNode, restype: PType) =
@ -1439,12 +1439,12 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
newNode(nkCall, n.info, quotes)])
result = semExpandToAst(c, result)
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
proc tryExpr(c: PContext, n: PNode,
flags: TExprFlags = {}, bufferErrors = false): PNode =
# watch out, hacks ahead:
let oldErrorCount = msgs.gErrorCounter
let oldErrorMax = msgs.gErrorMax
inc c.InCompilesContext
inc msgs.gSilence
# do not halt after first error:
msgs.gErrorMax = high(int)
@ -1453,6 +1453,8 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
openScope(c)
let oldOwnerLen = len(gOwners)
let oldGenerics = c.generics
let oldErrorOutputs = errorOutputs
errorOutputs = if bufferErrors: {eInMemory} else: {}
let oldContextLen = msgs.getInfoContextLen()
let oldInGenericContext = c.InGenericContext
@ -1475,7 +1477,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
setlen(gOwners, oldOwnerLen)
c.currentScope = oldScope
dec c.InCompilesContext
dec msgs.gSilence
errorOutputs = oldErrorOutputs
msgs.gErrorCounter = oldErrorCount
msgs.gErrorMax = oldErrorMax

View file

@ -132,7 +132,7 @@ proc fixNilType(n: PNode) =
for it in n: fixNilType(it)
n.typ = nil
proc discardCheck(result: PNode) =
proc discardCheck(c: PContext, result: PNode) =
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
if result.kind == nkNilLit:
result.typ = nil
@ -142,6 +142,10 @@ proc discardCheck(result: PNode) =
while n.kind in skipForDiscardable:
n = n.lastSon
n.typ = nil
elif c.InTypeClass > 0 and result.typ.kind == tyBool:
let verdict = semConstExpr(c, result)
if verdict.intVal == 0:
localError(result.info, "type class predicate failed.")
elif result.typ.kind != tyError and gCmd != cmdInteractive:
if result.typ.kind == tyNil:
fixNilType(result)
@ -169,7 +173,7 @@ proc semIf(c: PContext, n: PNode): PNode =
typ = commonType(typ, it.sons[0].typ)
else: illFormedAst(it)
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
for it in n: discardCheck(it.lastSon)
for it in n: discardCheck(c, it.lastSon)
result.kind = nkIfStmt
# propagate any enforced VoidContext:
if typ == EnforceVoidContext: result.typ = EnforceVoidContext
@ -230,7 +234,7 @@ proc semCase(c: PContext, n: PNode): PNode =
localError(n.info, errNotAllCasesCovered)
closeScope(c)
if isEmptyType(typ) or typ.kind == tyNil or not hasElse:
for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
# propagate any enforced VoidContext:
if typ == EnforceVoidContext:
result.typ = EnforceVoidContext
@ -275,8 +279,8 @@ proc semTry(c: PContext, n: PNode): PNode =
typ = commonType(typ, a.sons[length-1].typ)
dec c.p.inTryStmt
if isEmptyType(typ) or typ.kind == tyNil:
discardCheck(n.sons[0])
for i in 1..n.len-1: discardCheck(n.sons[i].lastSon)
discardCheck(c, n.sons[0])
for i in 1..n.len-1: discardCheck(c, n.sons[i].lastSon)
if typ == EnforceVoidContext:
result.typ = EnforceVoidContext
else:
@ -1221,7 +1225,7 @@ proc semStmtList(c: PContext, n: PNode): PNode =
voidContext = true
n.typ = EnforceVoidContext
if i != last or voidContext:
discardCheck(n.sons[i])
discardCheck(c, n.sons[i])
else:
n.typ = n.sons[i].typ
if not isEmptyType(n.typ):

View file

@ -85,6 +85,7 @@ proc initCandidate*(c: var TCandidate, callee: PSym, binding: PNode,
c.calleeSym = callee
c.calleeScope = calleeScope
initIdTable(c.bindings)
c.errors = nil
if binding != nil and callee.kind in RoutineKinds:
var typeParams = callee.ast[genericParamsPos]
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
@ -774,23 +775,16 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
addDecl(c, dummyParam)
for stmt in f.n[3]:
var e = c.semTryExpr(c, copyTree(stmt))
if e == nil:
let expStr = renderTree(stmt, {renderNoComments})
m.errors.safeAdd("can't compile " & expStr & " for " & a.typeToString)
return nil
var e = c.semTryExpr(c, copyTree(stmt), bufferErrors = false)
m.errors = bufferedMsgs
clearBufferedMsgs()
if e == nil: return nil
case e.kind
of nkReturnStmt:
nil
of nkReturnStmt: nil
of nkTypeSection: nil
of nkConstDef: nil
else:
if e.typ != nil and e.typ.kind == tyBool:
let verdict = c.semConstExpr(c, e)
if verdict.intVal == 0:
let expStr = renderTree(stmt, {renderNoComments})
m.errors.safeAdd(expStr & " doesn't hold for " & a.typeToString)
return nil
else: nil
result = arg
put(m.bindings, f, a)