proper error reporting for concepts and the introduction of the {.explain.} pragma
This commit is contained in:
parent
644d645ea7
commit
74a80988d9
13 changed files with 302 additions and 139 deletions
|
|
@ -252,6 +252,7 @@ type
|
||||||
sfProcvar, # proc can be passed to a proc var
|
sfProcvar, # proc can be passed to a proc var
|
||||||
sfDiscriminant, # field is a discriminant in a record/object
|
sfDiscriminant, # field is a discriminant in a record/object
|
||||||
sfDeprecated, # symbol is deprecated
|
sfDeprecated, # symbol is deprecated
|
||||||
|
sfExplain, # provide more diagnostics when this symbol is used
|
||||||
sfError, # usage of symbol should trigger a compile-time error
|
sfError, # usage of symbol should trigger a compile-time error
|
||||||
sfShadowed, # a symbol that was shadowed in some inner scope
|
sfShadowed, # a symbol that was shadowed in some inner scope
|
||||||
sfThread, # proc will run as a thread
|
sfThread, # proc will run as a thread
|
||||||
|
|
|
||||||
|
|
@ -499,7 +499,6 @@ type
|
||||||
TErrorOutput* = enum
|
TErrorOutput* = enum
|
||||||
eStdOut
|
eStdOut
|
||||||
eStdErr
|
eStdErr
|
||||||
eInMemory
|
|
||||||
|
|
||||||
TErrorOutputs* = set[TErrorOutput]
|
TErrorOutputs* = set[TErrorOutput]
|
||||||
|
|
||||||
|
|
@ -653,6 +652,15 @@ var
|
||||||
writelnHook*: proc (output: string) {.closure.}
|
writelnHook*: proc (output: string) {.closure.}
|
||||||
structuredErrorHook*: proc (info: TLineInfo; msg: string; severity: Severity) {.closure.}
|
structuredErrorHook*: proc (info: TLineInfo; msg: string; severity: Severity) {.closure.}
|
||||||
|
|
||||||
|
proc concat(strings: openarray[string]): string =
|
||||||
|
var totalLen = 0
|
||||||
|
for s in strings: totalLen += s.len
|
||||||
|
result = newStringOfCap totalLen
|
||||||
|
for s in strings: result.add s
|
||||||
|
|
||||||
|
template writeBufferedMsg(args: varargs[string, `$`]) =
|
||||||
|
bufferedMsgs.safeAdd concat(args)
|
||||||
|
|
||||||
proc suggestWriteln*(s: string) =
|
proc suggestWriteln*(s: string) =
|
||||||
if eStdOut in errorOutputs:
|
if eStdOut in errorOutputs:
|
||||||
if isNil(writelnHook):
|
if isNil(writelnHook):
|
||||||
|
|
@ -806,10 +814,7 @@ macro callStyledWriteLineStderr(args: varargs[typed]): untyped =
|
||||||
result.add(arg)
|
result.add(arg)
|
||||||
|
|
||||||
template callWritelnHook(args: varargs[string, `$`]) =
|
template callWritelnHook(args: varargs[string, `$`]) =
|
||||||
var s = ""
|
writelnHook concat(args)
|
||||||
for arg in args:
|
|
||||||
s.add arg
|
|
||||||
writelnHook s
|
|
||||||
|
|
||||||
template styledMsgWriteln*(args: varargs[typed]) =
|
template styledMsgWriteln*(args: varargs[typed]) =
|
||||||
if not isNil(writelnHook):
|
if not isNil(writelnHook):
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ proc parseSymbol*(p: var TParser, allowNil = false): PNode
|
||||||
proc parseTry(p: var TParser; isExpr: bool): PNode
|
proc parseTry(p: var TParser; isExpr: bool): PNode
|
||||||
proc parseCase(p: var TParser): PNode
|
proc parseCase(p: var TParser): PNode
|
||||||
proc parseStmtPragma(p: var TParser): PNode
|
proc parseStmtPragma(p: var TParser): PNode
|
||||||
|
proc parsePragma(p: var TParser): PNode
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
proc getTok(p: var TParser) =
|
proc getTok(p: var TParser) =
|
||||||
|
|
@ -770,6 +771,13 @@ proc parseOperators(p: var TParser, headNode: PNode,
|
||||||
|
|
||||||
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode =
|
||||||
result = primary(p, mode)
|
result = primary(p, mode)
|
||||||
|
if p.tok.tokType == tkCurlyDotLe and
|
||||||
|
p.lex.lineNumber == result.info.line and
|
||||||
|
mode == pmNormal:
|
||||||
|
var pragmaExp = newNodeP(nkPragmaExpr, p)
|
||||||
|
pragmaExp.addSon result
|
||||||
|
pragmaExp.addSon p.parsePragma
|
||||||
|
result = pragmaExp
|
||||||
result = parseOperators(p, result, limit, mode)
|
result = parseOperators(p, result, limit, mode)
|
||||||
|
|
||||||
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
|
proc simpleExpr(p: var TParser, mode = pmNormal): PNode =
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ const
|
||||||
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
||||||
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
||||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||||
wBorrow, wGcSafe, wExportNims, wPartial, wUsed}
|
wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain}
|
||||||
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
||||||
wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
|
wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
|
||||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||||
|
|
@ -73,7 +73,7 @@ const
|
||||||
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
|
proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
|
||||||
# implementation
|
# implementation
|
||||||
|
|
||||||
proc invalidPragma(n: PNode) =
|
proc invalidPragma*(n: PNode) =
|
||||||
localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
|
localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
|
||||||
|
|
||||||
proc pragmaAsm*(c: PContext, n: PNode): char =
|
proc pragmaAsm*(c: PContext, n: PNode): char =
|
||||||
|
|
@ -773,6 +773,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||||
of wProcVar:
|
of wProcVar:
|
||||||
noVal(it)
|
noVal(it)
|
||||||
incl(sym.flags, sfProcvar)
|
incl(sym.flags, sfProcvar)
|
||||||
|
of wExplain:
|
||||||
|
sym.flags.incl sfExplain
|
||||||
of wDeprecated:
|
of wDeprecated:
|
||||||
if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
|
if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
|
||||||
elif sym != nil: incl(sym.flags, sfDeprecated)
|
elif sym != nil: incl(sym.flags, sfDeprecated)
|
||||||
|
|
|
||||||
|
|
@ -55,76 +55,60 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||||
initialBinding: PNode,
|
initialBinding: PNode,
|
||||||
filter: TSymKinds,
|
filter: TSymKinds,
|
||||||
best, alt: var TCandidate,
|
best, alt: var TCandidate,
|
||||||
errors: var CandidateErrors) =
|
errors: var CandidateErrors,
|
||||||
|
diagnostics = false) =
|
||||||
var o: TOverloadIter
|
var o: TOverloadIter
|
||||||
var sym = initOverloadIter(o, c, headSymbol)
|
# thanks to the lazy semchecking for operands, we need to iterate over the
|
||||||
var scope = o.lastOverloadScope
|
# symbol table *before* any call to 'initCandidate' which might invoke
|
||||||
# Thanks to the lazy semchecking for operands, we need to check whether
|
# semExpr which might modify the symbol table in cases like
|
||||||
# 'initCandidate' modifies the symbol table (via semExpr).
|
# 'init(a, 1, (var b = new(Type2); b))'.
|
||||||
# This can occur in cases like 'init(a, 1, (var b = new(Type2); b))'
|
var symx = initOverloadIter(o, c, headSymbol)
|
||||||
let counterInitial = c.currentScope.symbols.counter
|
let symScope = o.lastOverloadScope
|
||||||
var syms: seq[tuple[s: PSym, scope: int]]
|
|
||||||
var nextSymIndex = 0
|
var syms: seq[tuple[a: PSym, b: int]] = @[]
|
||||||
while sym != nil:
|
while symx != nil:
|
||||||
if sym.kind in filter:
|
if symx.kind in filter:
|
||||||
# Initialise 'best' and 'alt' with the first available symbol
|
syms.add((symx, o.lastOverloadScope))
|
||||||
initCandidate(c, best, sym, initialBinding, scope)
|
symx = nextOverloadIter(o, c, headSymbol)
|
||||||
initCandidate(c, alt, sym, initialBinding, scope)
|
if syms.len == 0: return
|
||||||
best.state = csNoMatch
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
sym = nextOverloadIter(o, c, headSymbol)
|
|
||||||
scope = o.lastOverloadScope
|
|
||||||
var z: TCandidate
|
var z: TCandidate
|
||||||
while sym != nil:
|
initCandidate(c, best, syms[0][0], initialBinding,
|
||||||
if sym.kind notin filter:
|
symScope, diagnostics = diagnostics)
|
||||||
sym = nextOverloadIter(o, c, headSymbol)
|
initCandidate(c, alt, syms[0][0], initialBinding,
|
||||||
scope = o.lastOverloadScope
|
symScope, diagnostics = diagnostics)
|
||||||
continue
|
best.state = csNoMatch
|
||||||
|
|
||||||
|
for i in 0 .. <syms.len:
|
||||||
|
let sym = syms[i][0]
|
||||||
determineType(c, sym)
|
determineType(c, sym)
|
||||||
initCandidate(c, z, sym, initialBinding, scope)
|
initCandidate(c, z, sym, initialBinding,
|
||||||
if c.currentScope.symbols.counter == counterInitial or syms != nil:
|
syms[i][1], diagnostics = diagnostics)
|
||||||
matches(c, n, orig, z)
|
#if sym.name.s == "*" and (n.info ?? "temp5.nim") and n.info.line == 140:
|
||||||
if errors != nil:
|
# gDebug = true
|
||||||
errors.safeAdd((sym, int z.mutabilityProblem))
|
matches(c, n, orig, z)
|
||||||
if z.errors != nil:
|
if z.state == csMatch:
|
||||||
for err in z.errors:
|
# little hack so that iterators are preferred over everything else:
|
||||||
errors.add(err)
|
if sym.kind == skIterator: inc(z.exactMatches, 200)
|
||||||
if z.state == csMatch:
|
case best.state
|
||||||
# little hack so that iterators are preferred over everything else:
|
of csEmpty, csNoMatch: best = z
|
||||||
if sym.kind == skIterator: inc(z.exactMatches, 200)
|
of csMatch:
|
||||||
case best.state
|
var cmp = cmpCandidates(best, z)
|
||||||
of csEmpty, csNoMatch: best = z
|
if cmp < 0: best = z # x is better than the best so far
|
||||||
of csMatch:
|
elif cmp == 0: alt = z # x is as good as the best so far
|
||||||
var cmp = cmpCandidates(best, z)
|
else: discard
|
||||||
if cmp < 0: best = z # x is better than the best so far
|
#if sym.name.s == "cmp" and (n.info ?? "rstgen.nim") and n.info.line == 516:
|
||||||
elif cmp == 0: alt = z # x is as good as the best so far
|
# echo "Matches ", n.info, " ", typeToString(sym.typ)
|
||||||
else:
|
# debug sym
|
||||||
# Symbol table has been modified. Restart and pre-calculate all syms
|
# writeMatches(z)
|
||||||
# before any further candidate init and compare. SLOW, but rare case.
|
# for i in 1 .. <len(z.call):
|
||||||
syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, best, alt, o)
|
# z.call[i].typ.debug
|
||||||
if syms == nil:
|
# quit 1
|
||||||
sym = nextOverloadIter(o, c, headSymbol)
|
elif errors != nil or z.diagnostics != nil:
|
||||||
scope = o.lastOverloadScope
|
errors.safeAdd((sym, int z.mutabilityProblem, z.diagnostics))
|
||||||
elif nextSymIndex < syms.len:
|
|
||||||
# rare case: retrieve the next pre-calculated symbol
|
|
||||||
sym = syms[nextSymIndex].s
|
|
||||||
scope = syms[nextSymIndex].scope
|
|
||||||
nextSymIndex += 1
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
|
||||||
# Gives a detailed error message; this is separated from semOverloadedCall,
|
|
||||||
# as semOverlodedCall is already pretty slow (and we need this information
|
|
||||||
# only in case of an error).
|
|
||||||
if c.compilesContextId > 0:
|
|
||||||
# fail fast:
|
|
||||||
globalError(n.info, errTypeMismatch, "")
|
|
||||||
if errors.isNil or errors.len == 0:
|
|
||||||
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||||
|
(TPreferedDesc, string) =
|
||||||
|
var prefer = preferName
|
||||||
# to avoid confusing errors like:
|
# to avoid confusing errors like:
|
||||||
# got (SslPtr, SocketHandle)
|
# got (SslPtr, SocketHandle)
|
||||||
# but expected one of:
|
# but expected one of:
|
||||||
|
|
@ -132,9 +116,7 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||||
# we do a pre-analysis. If all types produce the same string, we will add
|
# we do a pre-analysis. If all types produce the same string, we will add
|
||||||
# module information.
|
# module information.
|
||||||
let proto = describeArgs(c, n, 1, preferName)
|
let proto = describeArgs(c, n, 1, preferName)
|
||||||
|
for err, mut, diagnostics in items(errors):
|
||||||
var prefer = preferName
|
|
||||||
for err, mut in items(errors):
|
|
||||||
var errProto = ""
|
var errProto = ""
|
||||||
let n = err.typ.n
|
let n = err.typ.n
|
||||||
for i in countup(1, n.len - 1):
|
for i in countup(1, n.len - 1):
|
||||||
|
|
@ -147,20 +129,36 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||||
prefer = preferModuleInfo
|
prefer = preferModuleInfo
|
||||||
break
|
break
|
||||||
|
|
||||||
# now use the information stored in 'prefer' to produce a nice error message:
|
|
||||||
var result = msgKindToString(errTypeMismatch)
|
|
||||||
add(result, describeArgs(c, n, 1, prefer))
|
|
||||||
add(result, ')')
|
|
||||||
var candidates = ""
|
var candidates = ""
|
||||||
for err, mut in items(errors):
|
for err, mut, diagnostics in items(errors):
|
||||||
if err.kind in routineKinds and err.ast != nil:
|
if err.kind in routineKinds and err.ast != nil:
|
||||||
add(candidates, renderTree(err.ast,
|
add(candidates, renderTree(err.ast,
|
||||||
{renderNoBody, renderNoComments,renderNoPragmas}))
|
{renderNoBody, renderNoComments, renderNoPragmas}))
|
||||||
else:
|
else:
|
||||||
add(candidates, err.getProcHeader(prefer))
|
add(candidates, err.getProcHeader(prefer))
|
||||||
add(candidates, "\n")
|
add(candidates, "\n")
|
||||||
if mut != 0 and mut < n.len:
|
if mut != 0 and mut < n.len:
|
||||||
add(candidates, "for a 'var' type a variable needs to be passed, but '" & renderTree(n[mut]) & "' is immutable\n")
|
add(candidates, "for a 'var' type a variable needs to be passed, but '" & renderTree(n[mut]) & "' is immutable\n")
|
||||||
|
for diag in diagnostics:
|
||||||
|
add(candidates, diag & "\n")
|
||||||
|
|
||||||
|
result = (prefer, candidates)
|
||||||
|
|
||||||
|
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||||
|
# Gives a detailed error message; this is separated from semOverloadedCall,
|
||||||
|
# as semOverlodedCall is already pretty slow (and we need this information
|
||||||
|
# only in case of an error).
|
||||||
|
if c.compilesContextId > 0:
|
||||||
|
# fail fast:
|
||||||
|
globalError(n.info, errTypeMismatch, "")
|
||||||
|
if errors.isNil or errors.len == 0:
|
||||||
|
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
||||||
|
return
|
||||||
|
|
||||||
|
let (prefer, candidates) = presentFailedCandidates(c, n, errors)
|
||||||
|
var result = msgKindToString(errTypeMismatch)
|
||||||
|
add(result, describeArgs(c, n, 1, prefer))
|
||||||
|
add(result, ')')
|
||||||
if candidates != "":
|
if candidates != "":
|
||||||
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
||||||
localError(n.info, errGenerated, result)
|
localError(n.info, errGenerated, result)
|
||||||
|
|
@ -172,7 +170,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||||
var symx = initOverloadIter(o, c, headSymbol)
|
var symx = initOverloadIter(o, c, headSymbol)
|
||||||
while symx != nil:
|
while symx != nil:
|
||||||
if symx.kind in routineKinds:
|
if symx.kind in routineKinds:
|
||||||
errors.add((symx, 0))
|
errors.add((symx, 0, nil))
|
||||||
symx = nextOverloadIter(o, c, headSymbol)
|
symx = nextOverloadIter(o, c, headSymbol)
|
||||||
if errors.len == 0:
|
if errors.len == 0:
|
||||||
localError(n.info, "could not resolve: " & $n)
|
localError(n.info, "could not resolve: " & $n)
|
||||||
|
|
@ -180,7 +178,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||||
notFoundError(c, n, errors)
|
notFoundError(c, n, errors)
|
||||||
|
|
||||||
proc resolveOverloads(c: PContext, n, orig: PNode,
|
proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
filter: TSymKinds;
|
filter: TSymKinds, flags: TExprFlags,
|
||||||
errors: var CandidateErrors): TCandidate =
|
errors: var CandidateErrors): TCandidate =
|
||||||
var initialBinding: PNode
|
var initialBinding: PNode
|
||||||
var alt: TCandidate
|
var alt: TCandidate
|
||||||
|
|
@ -194,7 +192,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
|
|
||||||
template pickBest(headSymbol) =
|
template pickBest(headSymbol) =
|
||||||
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
|
pickBestCandidate(c, headSymbol, n, orig, initialBinding,
|
||||||
filter, result, alt, errors)
|
filter, result, alt, errors, efExplain in flags)
|
||||||
pickBest(f)
|
pickBest(f)
|
||||||
|
|
||||||
let overloadsState = result.state
|
let overloadsState = result.state
|
||||||
|
|
@ -212,7 +210,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
if result.state != csMatch:
|
if result.state != csMatch:
|
||||||
n.sons.delete(1)
|
n.sons.delete(1)
|
||||||
orig.sons.delete(1)
|
orig.sons.delete(1)
|
||||||
excl n.flags, nfExprCall
|
|
||||||
else: return
|
else: return
|
||||||
|
|
||||||
if nfDotField in n.flags:
|
if nfDotField in n.flags:
|
||||||
|
|
@ -260,11 +257,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
# clean up the inserted ops
|
# clean up the inserted ops
|
||||||
n.sons.delete(2)
|
n.sons.delete(2)
|
||||||
n.sons[0] = f
|
n.sons[0] = f
|
||||||
|
|
||||||
errors = @[]
|
|
||||||
pickBest(f)
|
|
||||||
#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
|
||||||
not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
|
not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
|
||||||
|
|
@ -286,7 +278,6 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
|
getProcHeader(result.calleeSym), getProcHeader(alt.calleeSym),
|
||||||
args])
|
args])
|
||||||
|
|
||||||
|
|
||||||
proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
|
proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
|
||||||
if a.kind == nkHiddenCallConv and a.sons[0].kind == nkSym:
|
if a.kind == nkHiddenCallConv and a.sons[0].kind == nkSym:
|
||||||
let s = a.sons[0].sym
|
let s = a.sons[0].sym
|
||||||
|
|
@ -367,7 +358,7 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||||
|
|
||||||
proc canDeref(n: PNode): bool {.inline.} =
|
proc canDeref(n: PNode): bool {.inline.} =
|
||||||
result = n.len >= 2 and (let t = n[1].typ;
|
result = n.len >= 2 and (let t = n[1].typ;
|
||||||
t != nil and t.skipTypes({tyGenericInst, tyAlias}).kind in {tyPtr, tyRef})
|
t != nil and t.skipTypes({tyGenericInst}).kind in {tyPtr, tyRef})
|
||||||
|
|
||||||
proc tryDeref(n: PNode): PNode =
|
proc tryDeref(n: PNode): PNode =
|
||||||
result = newNodeI(nkHiddenDeref, n.info)
|
result = newNodeI(nkHiddenDeref, n.info)
|
||||||
|
|
@ -375,23 +366,40 @@ proc tryDeref(n: PNode): PNode =
|
||||||
result.addSon(n)
|
result.addSon(n)
|
||||||
|
|
||||||
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||||
filter: TSymKinds): PNode =
|
filter: TSymKinds, flags: TExprFlags): PNode =
|
||||||
var errors: CandidateErrors
|
var errors: CandidateErrors = if efExplain in flags: @[]
|
||||||
|
else: nil
|
||||||
var r = resolveOverloads(c, n, nOrig, filter, errors)
|
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
|
||||||
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
if r.state == csMatch:
|
||||||
|
# this may be triggered, when the explain pragma is used
|
||||||
|
if errors.len > 0:
|
||||||
|
let (_, candidates) = presentFailedCandidates(c, n, errors)
|
||||||
|
message(n.info, hintUser,
|
||||||
|
"Non-matching candidates for " & renderTree(n) & "\n" &
|
||||||
|
candidates)
|
||||||
|
result = semResolvedCall(c, n, r)
|
||||||
elif experimentalMode(c) and canDeref(n):
|
elif experimentalMode(c) and canDeref(n):
|
||||||
# try to deref the first argument and then try overloading resolution again:
|
# try to deref the first argument and then try overloading resolution again:
|
||||||
|
#
|
||||||
|
# XXX: why is this here?
|
||||||
|
# it could be added to the long list of alternatives tried
|
||||||
|
# inside `resolveOverloads` or it could be moved all the way
|
||||||
|
# into sigmatch with hidden conversion produced there
|
||||||
|
#
|
||||||
n.sons[1] = n.sons[1].tryDeref
|
n.sons[1] = n.sons[1].tryDeref
|
||||||
var r = resolveOverloads(c, n, nOrig, filter, errors)
|
var r = resolveOverloads(c, n, nOrig, filter, flags, errors)
|
||||||
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
||||||
else:
|
else:
|
||||||
# get rid of the deref again for a better error message:
|
# get rid of the deref again for a better error message:
|
||||||
n.sons[1] = n.sons[1].sons[0]
|
n.sons[1] = n.sons[1].sons[0]
|
||||||
notFoundError(c, n, errors)
|
notFoundError(c, n, errors)
|
||||||
else:
|
else:
|
||||||
notFoundError(c, n, errors)
|
if efExplain notin flags:
|
||||||
# else: result = errorNode(c, n)
|
# repeat the overload resolution,
|
||||||
|
# this time enabling all the diagnostic output (this should fail again)
|
||||||
|
discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
|
||||||
|
else:
|
||||||
|
notFoundError(c, n, errors)
|
||||||
|
|
||||||
proc explicitGenericInstError(n: PNode): PNode =
|
proc explicitGenericInstError(n: PNode): PNode =
|
||||||
localError(n.info, errCannotInstantiateX, renderTree(n))
|
localError(n.info, errCannotInstantiateX, renderTree(n))
|
||||||
|
|
@ -406,7 +414,12 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
|
let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
|
||||||
let arg = n[i].typ
|
let arg = n[i].typ
|
||||||
let tm = typeRel(m, formal, arg, true)
|
let tm = typeRel(m, formal, arg, true)
|
||||||
if tm in {isNone, isConvertible}: return nil
|
if tm in {isNone, isConvertible}:
|
||||||
|
if formal.sonsLen > 0 and formal.sons[0].kind != tyNone:
|
||||||
|
typeMismatch(n, formal.sons[0], arg)
|
||||||
|
else:
|
||||||
|
typeMismatch(n, formal, arg)
|
||||||
|
break
|
||||||
var newInst = generateInstance(c, s, m.bindings, n.info)
|
var newInst = generateInstance(c, s, m.bindings, n.info)
|
||||||
newInst.typ.flags.excl tfUnresolved
|
newInst.typ.flags.excl tfUnresolved
|
||||||
markUsed(n.info, s, c.graph.usageSym)
|
markUsed(n.info, s, c.graph.usageSym)
|
||||||
|
|
@ -428,7 +441,6 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
"; got " & $(n.len-1) & " type(s) but expected " & $expected)
|
"; got " & $(n.len-1) & " type(s) but expected " & $expected)
|
||||||
return n
|
return n
|
||||||
result = explicitGenericSym(c, n, s)
|
result = explicitGenericSym(c, n, s)
|
||||||
if result == nil: result = explicitGenericInstError(n)
|
|
||||||
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
|
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
|
||||||
# choose the generic proc with the proper number of type parameters.
|
# choose the generic proc with the proper number of type parameters.
|
||||||
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
|
# XXX I think this could be improved by reusing sigmatch.paramTypesMatch.
|
||||||
|
|
@ -441,12 +453,11 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
|
||||||
# it suffices that the candidate has the proper number of generic
|
# it suffices that the candidate has the proper number of generic
|
||||||
# type parameters:
|
# type parameters:
|
||||||
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
|
if safeLen(candidate.ast.sons[genericParamsPos]) == n.len-1:
|
||||||
let x = explicitGenericSym(c, n, candidate)
|
result.add(explicitGenericSym(c, n, candidate))
|
||||||
if x != nil: result.add(x)
|
|
||||||
# get rid of nkClosedSymChoice if not ambiguous:
|
# get rid of nkClosedSymChoice if not ambiguous:
|
||||||
if result.len == 1 and a.kind == nkClosedSymChoice:
|
if result.len == 1 and a.kind == nkClosedSymChoice:
|
||||||
result = result[0]
|
result = result[0]
|
||||||
elif result.len == 0: result = explicitGenericInstError(n)
|
# candidateCount != 1: return explicitGenericInstError(n)
|
||||||
else:
|
else:
|
||||||
result = explicitGenericInstError(n)
|
result = explicitGenericInstError(n)
|
||||||
|
|
||||||
|
|
@ -460,7 +471,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||||
call.add(newIdentNode(fn.name, fn.info))
|
call.add(newIdentNode(fn.name, fn.info))
|
||||||
for i in 1.. <fn.typ.n.len:
|
for i in 1.. <fn.typ.n.len:
|
||||||
let param = fn.typ.n.sons[i]
|
let param = fn.typ.n.sons[i]
|
||||||
let t = skipTypes(param.typ, abstractVar-{tyTypeDesc, tyDistinct})
|
let t = skipTypes(param.typ, abstractVar-{tyTypeDesc})
|
||||||
if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
|
if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
|
||||||
var x: PType
|
var x: PType
|
||||||
if param.typ.kind == tyVar:
|
if param.typ.kind == tyVar:
|
||||||
|
|
@ -470,12 +481,6 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
|
||||||
x = t.baseOfDistinct
|
x = t.baseOfDistinct
|
||||||
call.add(newNodeIT(nkEmpty, fn.info, x))
|
call.add(newNodeIT(nkEmpty, fn.info, x))
|
||||||
if hasDistinct:
|
if hasDistinct:
|
||||||
var resolved = semOverloadedCall(c, call, call, {fn.kind})
|
var resolved = semOverloadedCall(c, call, call, {fn.kind}, {})
|
||||||
if resolved != nil:
|
if resolved != nil:
|
||||||
result = resolved.sons[0].sym
|
result = resolved.sons[0].sym
|
||||||
if not compareTypes(result.typ.sons[0], fn.typ.sons[0], dcEqIgnoreDistinct):
|
|
||||||
result = nil
|
|
||||||
elif result.magic in {mArrPut, mArrGet}:
|
|
||||||
# cannot borrow these magics for now
|
|
||||||
result = nil
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,10 @@ type
|
||||||
|
|
||||||
TExprFlag* = enum
|
TExprFlag* = enum
|
||||||
efLValue, efWantIterator, efInTypeof,
|
efLValue, efWantIterator, efInTypeof,
|
||||||
efWantStmt, efAllowStmt, efDetermineType,
|
efWantStmt, efAllowStmt, efDetermineType, efExplain,
|
||||||
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
|
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
|
||||||
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo
|
efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo,
|
||||||
|
|
||||||
TExprFlags* = set[TExprFlag]
|
TExprFlags* = set[TExprFlag]
|
||||||
|
|
||||||
TTypeAttachedOp* = enum
|
TTypeAttachedOp* = enum
|
||||||
|
|
@ -84,12 +85,12 @@ type
|
||||||
libs*: seq[PLib] # all libs used by this module
|
libs*: seq[PLib] # all libs used by this module
|
||||||
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
|
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
|
||||||
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
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 = {}): PNode {.nimcall.}
|
||||||
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
|
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
|
||||||
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
||||||
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
|
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
|
||||||
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
|
||||||
filter: TSymKinds): PNode {.nimcall.}
|
filter: TSymKinds, flags: TExprFlags): PNode {.nimcall.}
|
||||||
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
|
||||||
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
|
semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
|
||||||
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
|
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
|
||||||
|
|
|
||||||
|
|
@ -643,10 +643,10 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
|
||||||
# for typeof support.
|
# for typeof support.
|
||||||
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
# for ``type(countup(1,3))``, see ``tests/ttoseq``.
|
||||||
result = semOverloadedCall(c, n, nOrig,
|
result = semOverloadedCall(c, n, nOrig,
|
||||||
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator})
|
{skProc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
|
||||||
else:
|
else:
|
||||||
result = semOverloadedCall(c, n, nOrig,
|
result = semOverloadedCall(c, n, nOrig,
|
||||||
{skProc, skMethod, skConverter, skMacro, skTemplate})
|
{skProc, skMethod, skConverter, skMacro, skTemplate}, flags)
|
||||||
|
|
||||||
if result != nil:
|
if result != nil:
|
||||||
if result.sons[0].kind != nkSym:
|
if result.sons[0].kind != nkSym:
|
||||||
|
|
@ -1744,7 +1744,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
let oldOwnerLen = len(c.graph.owners)
|
let oldOwnerLen = len(c.graph.owners)
|
||||||
let oldGenerics = c.generics
|
let oldGenerics = c.generics
|
||||||
let oldErrorOutputs = errorOutputs
|
let oldErrorOutputs = errorOutputs
|
||||||
errorOutputs = {}
|
if efExplain notin flags: errorOutputs = {}
|
||||||
let oldContextLen = msgs.getInfoContextLen()
|
let oldContextLen = msgs.getInfoContextLen()
|
||||||
|
|
||||||
let oldInGenericContext = c.inGenericContext
|
let oldInGenericContext = c.inGenericContext
|
||||||
|
|
@ -2350,8 +2350,20 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
of nkCurlyExpr:
|
of nkCurlyExpr:
|
||||||
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
|
result = semExpr(c, buildOverloadedSubscripts(n, getIdent"{}"), flags)
|
||||||
of nkPragmaExpr:
|
of nkPragmaExpr:
|
||||||
# which pragmas are allowed for expressions? `likely`, `unlikely`
|
var
|
||||||
internalError(n.info, "semExpr() to implement") # XXX: to implement
|
expr = n[0]
|
||||||
|
pragma = n[1]
|
||||||
|
pragmaName = considerQuotedIdent(pragma[0])
|
||||||
|
flags = flags
|
||||||
|
|
||||||
|
case whichKeyword(pragmaName)
|
||||||
|
of wExplain:
|
||||||
|
flags.incl efExplain
|
||||||
|
else:
|
||||||
|
# what other pragmas are allowed for expressions? `likely`, `unlikely`
|
||||||
|
invalidPragma(n)
|
||||||
|
|
||||||
|
result = semExpr(c, n[0], flags)
|
||||||
of nkPar:
|
of nkPar:
|
||||||
case checkPar(n)
|
case checkPar(n)
|
||||||
of paNone: result = errorNode(c, n)
|
of paNone: result = errorNode(c, n)
|
||||||
|
|
|
||||||
|
|
@ -766,6 +766,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
let owner = if typeClass.sym != nil: typeClass.sym
|
let owner = if typeClass.sym != nil: typeClass.sym
|
||||||
else: getCurrOwner(c)
|
else: getCurrOwner(c)
|
||||||
var s = newSym(skType, finalTypId, owner, info)
|
var s = newSym(skType, finalTypId, owner, info)
|
||||||
|
if sfExplain in owner.flags: s.flags.incl sfExplain
|
||||||
if typId == nil: s.flags.incl(sfAnon)
|
if typId == nil: s.flags.incl(sfAnon)
|
||||||
s.linkTo(typeClass)
|
s.linkTo(typeClass)
|
||||||
typeClass.flags.incl tfImplicitTypeParam
|
typeClass.flags.incl tfImplicitTypeParam
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
if isTypeParam(n[i]): needsFixing = true
|
if isTypeParam(n[i]): needsFixing = true
|
||||||
if needsFixing:
|
if needsFixing:
|
||||||
n.sons[0] = newSymNode(n.sons[0].sym.owner)
|
n.sons[0] = newSymNode(n.sons[0].sym.owner)
|
||||||
return cl.c.semOverloadedCall(cl.c, n, n, {skProc})
|
return cl.c.semOverloadedCall(cl.c, n, n, {skProc}, {})
|
||||||
|
|
||||||
for i in 0 .. <n.safeLen:
|
for i in 0 .. <n.safeLen:
|
||||||
n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
|
n.sons[i] = reResolveCallsWithTypedescParams(cl, n[i])
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,13 @@ type
|
||||||
TCandidateState* = enum
|
TCandidateState* = enum
|
||||||
csEmpty, csMatch, csNoMatch
|
csEmpty, csMatch, csNoMatch
|
||||||
|
|
||||||
CandidateErrors* = seq[(PSym,int)]
|
CandidateError = tuple
|
||||||
|
sym: PSym
|
||||||
|
unmatchedVarParam: int
|
||||||
|
diagnostics: seq[string]
|
||||||
|
|
||||||
|
CandidateErrors* = seq[CandidateError]
|
||||||
|
|
||||||
TCandidate* = object
|
TCandidate* = object
|
||||||
c*: PContext
|
c*: PContext
|
||||||
exactMatches*: int # also misused to prefer iters over procs
|
exactMatches*: int # also misused to prefer iters over procs
|
||||||
|
|
@ -53,11 +59,16 @@ type
|
||||||
# matching. they will be reset if the matching
|
# matching. they will be reset if the matching
|
||||||
# is not successful. may replace the bindings
|
# is not successful. may replace the bindings
|
||||||
# table in the future.
|
# table in the future.
|
||||||
|
diagnostics*: seq[string] # when this is not nil, the matching process
|
||||||
|
# will collect extra diagnostics that will be
|
||||||
|
# displayed to the user.
|
||||||
|
# triggered when overload resolution fails
|
||||||
|
# or when the explain pragma is used. may be
|
||||||
|
# triggered with an idetools command in the
|
||||||
|
# future.
|
||||||
mutabilityProblem*: uint8 # tyVar mismatch
|
mutabilityProblem*: uint8 # tyVar mismatch
|
||||||
inheritancePenalty: int # to prefer closest father object type
|
inheritancePenalty: int # to prefer closest father object type
|
||||||
errors*: CandidateErrors # additional clarifications to be displayed to the
|
|
||||||
# user if overload resolution fails
|
|
||||||
|
|
||||||
TTypeRelation* = enum # order is important!
|
TTypeRelation* = enum # order is important!
|
||||||
isNone, isConvertible,
|
isNone, isConvertible,
|
||||||
isIntConv,
|
isIntConv,
|
||||||
|
|
@ -105,7 +116,7 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
|
||||||
idTablePut(c.bindings, key, val.skipIntLit)
|
idTablePut(c.bindings, key, val.skipIntLit)
|
||||||
|
|
||||||
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
||||||
binding: PNode, calleeScope = -1) =
|
binding: PNode, calleeScope = -1, diagnostics = false) =
|
||||||
initCandidateAux(ctx, c, callee.typ)
|
initCandidateAux(ctx, c, callee.typ)
|
||||||
c.calleeSym = callee
|
c.calleeSym = callee
|
||||||
if callee.kind in skProcKinds and calleeScope == -1:
|
if callee.kind in skProcKinds and calleeScope == -1:
|
||||||
|
|
@ -120,9 +131,9 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
|
||||||
c.calleeScope = 1
|
c.calleeScope = 1
|
||||||
else:
|
else:
|
||||||
c.calleeScope = calleeScope
|
c.calleeScope = calleeScope
|
||||||
|
c.diagnostics = if diagnostics: @[] else: nil
|
||||||
c.magic = c.calleeSym.magic
|
c.magic = c.calleeSym.magic
|
||||||
initIdTable(c.bindings)
|
initIdTable(c.bindings)
|
||||||
c.errors = nil
|
|
||||||
if binding != nil and callee.kind in routineKinds:
|
if binding != nil and callee.kind in routineKinds:
|
||||||
var typeParams = callee.ast[genericParamsPos]
|
var typeParams = callee.ast[genericParamsPos]
|
||||||
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
|
for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
|
||||||
|
|
@ -577,14 +588,15 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
|
||||||
|
|
||||||
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
ff, a: PType): PType =
|
ff, a: PType): PType =
|
||||||
var body = ff.skipTypes({tyUserTypeClassInst})
|
var
|
||||||
|
Concept = ff.skipTypes({tyUserTypeClassInst})
|
||||||
|
body = Concept.n[3]
|
||||||
if c.inTypeClass > 4:
|
if c.inTypeClass > 4:
|
||||||
localError(body.n[3].info, $body.n[3] & " too nested for type matching")
|
localError(body.info, $body & " too nested for type matching")
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
openScope(c)
|
openScope(c)
|
||||||
inc c.inTypeClass
|
inc c.inTypeClass
|
||||||
|
|
||||||
defer:
|
defer:
|
||||||
dec c.inTypeClass
|
dec c.inTypeClass
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
@ -599,7 +611,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
param: PSym
|
param: PSym
|
||||||
|
|
||||||
template paramSym(kind): untyped =
|
template paramSym(kind): untyped =
|
||||||
newSym(kind, typeParamName, body.sym, body.sym.info)
|
newSym(kind, typeParamName, Concept.sym, Concept.sym.info)
|
||||||
|
|
||||||
case typ.kind
|
case typ.kind
|
||||||
of tyStatic:
|
of tyStatic:
|
||||||
|
|
@ -622,7 +634,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
addDecl(c, param)
|
addDecl(c, param)
|
||||||
typeParams.safeAdd((param, typ))
|
typeParams.safeAdd((param, typ))
|
||||||
|
|
||||||
for param in body.n[0]:
|
for param in Concept.n[0]:
|
||||||
var
|
var
|
||||||
dummyName: PNode
|
dummyName: PNode
|
||||||
dummyType: PType
|
dummyType: PType
|
||||||
|
|
@ -645,11 +657,31 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
|
|
||||||
internalAssert dummyName.kind == nkIdent
|
internalAssert dummyName.kind == nkIdent
|
||||||
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
|
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
|
||||||
dummyName.ident, body.sym, body.sym.info)
|
dummyName.ident, Concept.sym, Concept.sym.info)
|
||||||
dummyParam.typ = dummyType
|
dummyParam.typ = dummyType
|
||||||
addDecl(c, dummyParam)
|
addDecl(c, dummyParam)
|
||||||
|
|
||||||
var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
|
var
|
||||||
|
oldWriteHook: type(writelnHook)
|
||||||
|
diagnostics: seq[string]
|
||||||
|
flags: TExprFlags = {}
|
||||||
|
collectDiagnostics = m.diagnostics != nil or
|
||||||
|
sfExplain in Concept.sym.flags
|
||||||
|
|
||||||
|
if collectDiagnostics:
|
||||||
|
oldWriteHook = writelnHook
|
||||||
|
# XXX: we can't write to m.diagnostics directly, because
|
||||||
|
# Nim doesn't support capturing var params in closures
|
||||||
|
diagnostics = @[]
|
||||||
|
writelnHook = proc (s: string) = diagnostics.add(s)
|
||||||
|
flags = {efExplain}
|
||||||
|
|
||||||
|
var checkedBody = c.semTryExpr(c, body.copyTree, flags)
|
||||||
|
|
||||||
|
if collectDiagnostics:
|
||||||
|
writelnHook = oldWriteHook
|
||||||
|
for msg in diagnostics: m.diagnostics.safeAdd msg
|
||||||
|
|
||||||
if checkedBody == nil: return nil
|
if checkedBody == nil: return nil
|
||||||
|
|
||||||
# The inferrable type params have been identified during the semTryExpr above.
|
# The inferrable type params have been identified during the semTryExpr above.
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ type
|
||||||
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit,
|
||||||
wAsmNoStackFrame,
|
wAsmNoStackFrame,
|
||||||
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
|
wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks,
|
||||||
wPartial,
|
wPartial, wExplain,
|
||||||
|
|
||||||
wAuto, wBool, wCatch, wChar, wClass,
|
wAuto, wBool, wCatch, wChar, wClass,
|
||||||
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
|
wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast,
|
||||||
|
|
@ -152,7 +152,7 @@ const
|
||||||
"computedgoto", "injectstmt", "experimental",
|
"computedgoto", "injectstmt", "experimental",
|
||||||
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
"write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit",
|
||||||
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
|
"asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked",
|
||||||
"guard", "locks", "partial",
|
"guard", "locks", "partial", "explain",
|
||||||
|
|
||||||
"auto", "bool", "catch", "char", "class",
|
"auto", "bool", "catch", "char", "class",
|
||||||
"const_cast", "default", "delete", "double",
|
"const_cast", "default", "delete", "double",
|
||||||
|
|
|
||||||
92
tests/concepts/texplain.nim
Normal file
92
tests/concepts/texplain.nim
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c --verbosity:0 --colors:off $file"
|
||||||
|
nimout: '''
|
||||||
|
tests/concepts/texplain.nim(71, 10) Hint: Non-matching candidates for e(y)
|
||||||
|
proc e(i: int): int
|
||||||
|
[User]
|
||||||
|
tests/concepts/texplain.nim(74, 7) Hint: Non-matching candidates for e(10)
|
||||||
|
proc e[ExplainedConcept](o: ExplainedConcept): int
|
||||||
|
tests/concepts/texplain.nim(38, 6) Error: undeclared field: 'foo'
|
||||||
|
tests/concepts/texplain.nim(38, 6) Error: undeclared field: '.'
|
||||||
|
tests/concepts/texplain.nim(38, 6) Error: type mismatch: got (
|
||||||
|
[User]
|
||||||
|
tests/concepts/texplain.nim(77, 10) Hint: Non-matching candidates for e(10)
|
||||||
|
proc e[ExplainedConcept](o: ExplainedConcept): int
|
||||||
|
tests/concepts/texplain.nim(38, 6) Error: undeclared field: 'foo'
|
||||||
|
tests/concepts/texplain.nim(38, 6) Error: undeclared field: '.'
|
||||||
|
tests/concepts/texplain.nim(38, 6) Error: type mismatch: got (
|
||||||
|
[User]
|
||||||
|
tests/concepts/texplain.nim(81, 20) Error: type mismatch: got (
|
||||||
|
tests/concepts/texplain.nim(82, 20) Error: type mismatch: got (
|
||||||
|
tests/concepts/texplain.nim(83, 20) Hint: Non-matching candidates for r(y)
|
||||||
|
proc r(i: string): int
|
||||||
|
[User]
|
||||||
|
tests/concepts/texplain.nim(91, 2) Error: type mismatch: got (MatchingType)
|
||||||
|
but expected one of:
|
||||||
|
proc f[NestedConcept](o: NestedConcept)
|
||||||
|
tests/concepts/texplain.nim(42, 6) Error: undeclared field: 'foo'
|
||||||
|
tests/concepts/texplain.nim(42, 6) Error: undeclared field: '.'
|
||||||
|
tests/concepts/texplain.nim(42, 6) Error: type mismatch: got (
|
||||||
|
tests/concepts/texplain.nim(46, 5) Error: type class predicate failed
|
||||||
|
'''
|
||||||
|
line: 46
|
||||||
|
errormsg: "type class predicate failed"
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
ExplainedConcept {.explain.} = concept o
|
||||||
|
o.foo is int
|
||||||
|
o.bar is string
|
||||||
|
|
||||||
|
RegularConcept = concept o
|
||||||
|
o.foo is int
|
||||||
|
o.bar is string
|
||||||
|
|
||||||
|
NestedConcept = concept o
|
||||||
|
o.foo is RegularConcept
|
||||||
|
|
||||||
|
NonMatchingType = object
|
||||||
|
foo: int
|
||||||
|
bar: int
|
||||||
|
|
||||||
|
MatchingType = object
|
||||||
|
foo: int
|
||||||
|
bar: string
|
||||||
|
|
||||||
|
proc e(o: ExplainedConcept): int = 1
|
||||||
|
proc e(i: int): int = i
|
||||||
|
|
||||||
|
proc r(o: RegularConcept): int = 1
|
||||||
|
proc r(i: string): int = 1
|
||||||
|
|
||||||
|
proc f(o: NestedConcept) = discard
|
||||||
|
|
||||||
|
var n = NonMatchingType(foo: 10, bar: 20)
|
||||||
|
var y = MatchingType(foo: 10, bar: "bar")
|
||||||
|
|
||||||
|
# no diagnostic here:
|
||||||
|
discard e(y)
|
||||||
|
|
||||||
|
# explain that e(int) doesn't match
|
||||||
|
discard e(y) {.explain.}
|
||||||
|
|
||||||
|
# explain that e(ExplainedConcept) doesn't match
|
||||||
|
echo(e(10) {.explain.}, 20)
|
||||||
|
|
||||||
|
# explain that e(ExplainedConcept) doesn't again
|
||||||
|
discard e(10)
|
||||||
|
|
||||||
|
static:
|
||||||
|
# provide diagnostics why the compile block failed
|
||||||
|
assert(compiles(e(n)) {.explain.} == false)
|
||||||
|
assert(compiles(r(n)) {.explain.} == false)
|
||||||
|
assert(compiles(r(y)) {.explain.} == true)
|
||||||
|
|
||||||
|
# these should not produce any output
|
||||||
|
assert(compiles(r(10)) == false)
|
||||||
|
assert(compiles(e(10)) == true)
|
||||||
|
|
||||||
|
# finally, provide multiple nested explanations for failed matching
|
||||||
|
# of regular concepts, even when the explain pragma is not used
|
||||||
|
f(y)
|
||||||
|
|
||||||
|
|
@ -184,6 +184,8 @@ proc addResult(r: var TResults, test: TTest,
|
||||||
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
|
proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest) =
|
||||||
if strip(expected.msg) notin strip(given.msg):
|
if strip(expected.msg) notin strip(given.msg):
|
||||||
r.addResult(test, expected.msg, given.msg, reMsgsDiffer)
|
r.addResult(test, expected.msg, given.msg, reMsgsDiffer)
|
||||||
|
elif expected.nimout.len > 0 and expected.nimout.normalize notin given.nimout.normalize:
|
||||||
|
r.addResult(test, expected.nimout, given.nimout, reMsgsDiffer)
|
||||||
elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and
|
elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and
|
||||||
"internal error:" notin expected.msg:
|
"internal error:" notin expected.msg:
|
||||||
r.addResult(test, expected.file, given.file, reFilesDiffer)
|
r.addResult(test, expected.file, given.file, reFilesDiffer)
|
||||||
|
|
@ -233,6 +235,8 @@ proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) =
|
||||||
if exp notin giv:
|
if exp notin giv:
|
||||||
given.err = reMsgsDiffer
|
given.err = reMsgsDiffer
|
||||||
|
|
||||||
|
proc normalize(s: string): string = s.strip.replace("\C\L", "\L")
|
||||||
|
|
||||||
proc makeDeterministic(s: string): string =
|
proc makeDeterministic(s: string): string =
|
||||||
var x = splitLines(s)
|
var x = splitLines(s)
|
||||||
sort(x, system.cmp)
|
sort(x, system.cmp)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue