This commit is contained in:
parent
b50ae6817a
commit
a6526695f0
2 changed files with 68 additions and 51 deletions
|
|
@ -107,7 +107,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||||
elif errorsEnabled or z.diagnosticsEnabled:
|
elif errorsEnabled or z.diagnosticsEnabled:
|
||||||
errors.add(CandidateError(
|
errors.add(CandidateError(
|
||||||
sym: sym,
|
sym: sym,
|
||||||
unmatchedVarParam: int z.mutabilityProblem,
|
|
||||||
firstMismatch: z.firstMismatch,
|
firstMismatch: z.firstMismatch,
|
||||||
diagnostics: z.diagnostics))
|
diagnostics: z.diagnostics))
|
||||||
else:
|
else:
|
||||||
|
|
@ -173,14 +172,14 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||||
var filterOnlyFirst = false
|
var filterOnlyFirst = false
|
||||||
if optShowAllMismatches notin c.config.globalOptions:
|
if optShowAllMismatches notin c.config.globalOptions:
|
||||||
for err in errors:
|
for err in errors:
|
||||||
if err.firstMismatch > 1:
|
if err.firstMismatch.arg > 1:
|
||||||
filterOnlyFirst = true
|
filterOnlyFirst = true
|
||||||
break
|
break
|
||||||
|
|
||||||
var candidates = ""
|
var candidates = ""
|
||||||
var skipped = 0
|
var skipped = 0
|
||||||
for err in errors:
|
for err in errors:
|
||||||
if filterOnlyFirst and err.firstMismatch == 1:
|
if filterOnlyFirst and err.firstMismatch.arg == 1:
|
||||||
inc skipped
|
inc skipped
|
||||||
continue
|
continue
|
||||||
if err.sym.kind in routineKinds and err.sym.ast != nil:
|
if err.sym.kind in routineKinds and err.sym.ast != nil:
|
||||||
|
|
@ -189,34 +188,35 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
|
||||||
else:
|
else:
|
||||||
add(candidates, getProcHeader(c.config, err.sym, prefer))
|
add(candidates, getProcHeader(c.config, err.sym, prefer))
|
||||||
add(candidates, "\n")
|
add(candidates, "\n")
|
||||||
if err.firstMismatch != 0 and n.len > 1:
|
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
|
||||||
let cond = n.len > 2
|
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
|
||||||
if cond:
|
if n.len > 1:
|
||||||
candidates.add(" first type mismatch at position: " & $abs(err.firstMismatch))
|
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
|
||||||
if err.firstMismatch >= 0: candidates.add("\n required type: ")
|
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
|
||||||
else: candidates.add("\n unknown named parameter: " & $n[-err.firstMismatch][0])
|
case err.firstMismatch.kind
|
||||||
var wanted, got: PType = nil
|
of kUnknownNamedParam: candidates.add("\n unknown named parameter: " & $nArg[0])
|
||||||
if err.firstMismatch < 0:
|
of kAlreadyGiven: candidates.add("\n named param already provided: " & $nArg[0])
|
||||||
discard
|
of kExtraArg: candidates.add("\n extra argument given")
|
||||||
elif err.firstMismatch < err.sym.typ.len:
|
of kMissingParam: candidates.add("\n missing parameter: " & nameParam)
|
||||||
wanted = err.sym.typ.sons[err.firstMismatch]
|
of kTypeMismatch, kVarNeeded:
|
||||||
if cond: candidates.add typeToString(wanted)
|
doAssert nArg != nil
|
||||||
else:
|
var wanted = err.firstMismatch.formal.typ
|
||||||
if cond: candidates.add "none"
|
doAssert err.firstMismatch.formal != nil
|
||||||
if err.firstMismatch > 0 and err.firstMismatch < n.len:
|
candidates.add("\n required type for " & nameParam & ": ")
|
||||||
if cond:
|
candidates.add typeToString(wanted)
|
||||||
candidates.add "\n but expression '"
|
candidates.add "\n but expression '"
|
||||||
candidates.add renderTree(n[err.firstMismatch])
|
if err.firstMismatch.kind == kVarNeeded:
|
||||||
|
candidates.add renderNotLValue(nArg)
|
||||||
|
candidates.add "' is immutable, not 'var'"
|
||||||
|
else:
|
||||||
|
candidates.add renderTree(nArg)
|
||||||
candidates.add "' is of type: "
|
candidates.add "' is of type: "
|
||||||
got = n[err.firstMismatch].typ
|
var got = nArg.typ
|
||||||
if cond: candidates.add typeToString(got)
|
candidates.add typeToString(got)
|
||||||
if wanted != nil and got != nil:
|
doAssert wanted != nil
|
||||||
effectProblem(wanted, got, candidates)
|
if got != nil: effectProblem(wanted, got, candidates)
|
||||||
if cond: candidates.add "\n"
|
of kUnknown: internalAssert(c.config, false)
|
||||||
if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
|
candidates.add "\n"
|
||||||
candidates.add(" for a 'var' type a variable needs to be passed, but '" &
|
|
||||||
renderNotLValue(n[err.unmatchedVarParam]) &
|
|
||||||
"' is immutable\n")
|
|
||||||
for diag in err.diagnostics:
|
for diag in err.diagnostics:
|
||||||
candidates.add(diag & "\n")
|
candidates.add(diag & "\n")
|
||||||
if skipped > 0:
|
if skipped > 0:
|
||||||
|
|
@ -260,7 +260,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
|
||||||
while symx != nil:
|
while symx != nil:
|
||||||
if symx.kind in routineKinds:
|
if symx.kind in routineKinds:
|
||||||
errors.add(CandidateError(sym: symx,
|
errors.add(CandidateError(sym: symx,
|
||||||
unmatchedVarParam: 0, firstMismatch: 0,
|
firstMismatch: MismatchInfo(),
|
||||||
diagnostics: @[],
|
diagnostics: @[],
|
||||||
enabled: false))
|
enabled: false))
|
||||||
symx = nextOverloadIter(o, c, headSymbol)
|
symx = nextOverloadIter(o, c, headSymbol)
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,22 @@ when (defined(booting) or defined(nimsuggest)) and not defined(leanCompiler):
|
||||||
import docgen
|
import docgen
|
||||||
|
|
||||||
type
|
type
|
||||||
|
MismatchKind* = enum
|
||||||
|
kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
|
||||||
|
kMissingParam, kExtraArg
|
||||||
|
|
||||||
|
MismatchInfo* = object
|
||||||
|
kind*: MismatchKind # reason for mismatch
|
||||||
|
arg*: int # position of provided arguments that mismatches
|
||||||
|
formal*: PSym # parameter that mismatches against provided argument
|
||||||
|
# its position can differ from `arg` because of varargs
|
||||||
|
|
||||||
TCandidateState* = enum
|
TCandidateState* = enum
|
||||||
csEmpty, csMatch, csNoMatch
|
csEmpty, csMatch, csNoMatch
|
||||||
|
|
||||||
CandidateError* = object
|
CandidateError* = object
|
||||||
sym*: PSym
|
sym*: PSym
|
||||||
unmatchedVarParam*, firstMismatch*: int
|
firstMismatch*: MismatchInfo
|
||||||
diagnostics*: seq[string]
|
diagnostics*: seq[string]
|
||||||
enabled*: bool
|
enabled*: bool
|
||||||
|
|
||||||
|
|
@ -56,7 +66,6 @@ type
|
||||||
# a distrinct type
|
# a distrinct type
|
||||||
typedescMatched*: bool
|
typedescMatched*: bool
|
||||||
isNoCall*: bool # misused for generic type instantiations C[T]
|
isNoCall*: bool # misused for generic type instantiations C[T]
|
||||||
mutabilityProblem*: uint8 # tyVar mismatch
|
|
||||||
inferredTypes: seq[PType] # inferred types during the current signature
|
inferredTypes: seq[PType] # inferred types during the current signature
|
||||||
# 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
|
||||||
|
|
@ -70,8 +79,7 @@ type
|
||||||
# triggered with an idetools command in the
|
# triggered with an idetools command in the
|
||||||
# future.
|
# future.
|
||||||
inheritancePenalty: int # to prefer closest father object type
|
inheritancePenalty: int # to prefer closest father object type
|
||||||
firstMismatch*: int # position of the first type mismatch for
|
firstMismatch*: MismatchInfo # mismatch info for better error messages
|
||||||
# better error messages
|
|
||||||
diagnosticsEnabled*: bool
|
diagnosticsEnabled*: bool
|
||||||
|
|
||||||
TTypeRelFlag* = enum
|
TTypeRelFlag* = enum
|
||||||
|
|
@ -112,6 +120,7 @@ proc initCandidateAux(ctx: PContext,
|
||||||
c.intConvMatches = 0
|
c.intConvMatches = 0
|
||||||
c.genericMatches = 0
|
c.genericMatches = 0
|
||||||
c.state = csEmpty
|
c.state = csEmpty
|
||||||
|
c.firstMismatch = MismatchInfo()
|
||||||
c.callee = callee
|
c.callee = callee
|
||||||
c.call = nil
|
c.call = nil
|
||||||
c.baseTypeMatch = false
|
c.baseTypeMatch = false
|
||||||
|
|
@ -2279,6 +2288,17 @@ template isVarargsUntyped(x): untyped =
|
||||||
|
|
||||||
proc matchesAux(c: PContext, n, nOrig: PNode,
|
proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
m: var TCandidate, marker: var IntSet) =
|
m: var TCandidate, marker: var IntSet) =
|
||||||
|
var
|
||||||
|
a = 1 # iterates over the actual given arguments
|
||||||
|
f = if m.callee.kind != tyGenericBody: 1
|
||||||
|
else: 0 # iterates over formal parameters
|
||||||
|
arg: PNode # current prepared argument
|
||||||
|
formal: PSym # current routine parameter
|
||||||
|
|
||||||
|
defer:
|
||||||
|
m.firstMismatch.arg = a
|
||||||
|
m.firstMismatch.formal = formal
|
||||||
|
|
||||||
template checkConstraint(n: untyped) {.dirty.} =
|
template checkConstraint(n: untyped) {.dirty.} =
|
||||||
if not formal.constraint.isNil:
|
if not formal.constraint.isNil:
|
||||||
if matchNodeKinds(formal.constraint, n):
|
if matchNodeKinds(formal.constraint, n):
|
||||||
|
|
@ -2293,28 +2313,21 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
if argConverter.kind == nkHiddenCallConv:
|
if argConverter.kind == nkHiddenCallConv:
|
||||||
if argConverter.typ.kind != tyVar:
|
if argConverter.typ.kind != tyVar:
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.mutabilityProblem = uint8(f-1)
|
m.firstMismatch.kind = kVarNeeded
|
||||||
return
|
return
|
||||||
elif not n.isLValue:
|
elif not n.isLValue:
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.mutabilityProblem = uint8(f-1)
|
m.firstMismatch.kind = kVarNeeded
|
||||||
return
|
return
|
||||||
|
|
||||||
var
|
|
||||||
# iterates over formal parameters
|
|
||||||
f = if m.callee.kind != tyGenericBody: 1
|
|
||||||
else: 0
|
|
||||||
# iterates over the actual given arguments
|
|
||||||
a = 1
|
|
||||||
arg: PNode # current prepared argument
|
|
||||||
|
|
||||||
m.state = csMatch # until proven otherwise
|
m.state = csMatch # until proven otherwise
|
||||||
|
m.firstMismatch = MismatchInfo()
|
||||||
m.call = newNodeI(n.kind, n.info)
|
m.call = newNodeI(n.kind, n.info)
|
||||||
m.call.typ = base(m.callee) # may be nil
|
m.call.typ = base(m.callee) # may be nil
|
||||||
var formalLen = m.callee.n.len
|
var formalLen = m.callee.n.len
|
||||||
addSon(m.call, copyTree(n.sons[0]))
|
addSon(m.call, copyTree(n.sons[0]))
|
||||||
var container: PNode = nil # constructed container
|
var container: PNode = nil # constructed container
|
||||||
var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
|
formal = if formalLen > 1: m.callee.n.sons[1].sym else: nil
|
||||||
|
|
||||||
while a < n.len:
|
while a < n.len:
|
||||||
if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped:
|
if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped:
|
||||||
|
|
@ -2335,20 +2348,20 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
addSon(container, n.sons[a])
|
addSon(container, n.sons[a])
|
||||||
elif n.sons[a].kind == nkExprEqExpr:
|
elif n.sons[a].kind == nkExprEqExpr:
|
||||||
# named param
|
# named param
|
||||||
|
m.firstMismatch.kind = kUnknownNamedParam
|
||||||
# check if m.callee has such a param:
|
# check if m.callee has such a param:
|
||||||
prepareNamedParam(n.sons[a], c)
|
prepareNamedParam(n.sons[a], c)
|
||||||
if n.sons[a].sons[0].kind != nkIdent:
|
if n.sons[a].sons[0].kind != nkIdent:
|
||||||
localError(c.config, n.sons[a].info, "named parameter has to be an identifier")
|
localError(c.config, n.sons[a].info, "named parameter has to be an identifier")
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.firstMismatch = -a
|
|
||||||
return
|
return
|
||||||
formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
|
formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
|
||||||
if formal == nil:
|
if formal == nil:
|
||||||
# no error message!
|
# no error message!
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.firstMismatch = -a
|
|
||||||
return
|
return
|
||||||
if containsOrIncl(marker, formal.position):
|
if containsOrIncl(marker, formal.position):
|
||||||
|
m.firstMismatch.kind = kAlreadyGiven
|
||||||
# already in namedParams, so no match
|
# already in namedParams, so no match
|
||||||
# we used to produce 'errCannotBindXTwice' here but see
|
# we used to produce 'errCannotBindXTwice' here but see
|
||||||
# bug #3836 of why that is not sound (other overload with
|
# bug #3836 of why that is not sound (other overload with
|
||||||
|
|
@ -2362,9 +2375,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
n.sons[a].typ = n.sons[a].sons[1].typ
|
n.sons[a].typ = n.sons[a].sons[1].typ
|
||||||
arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
|
arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
|
||||||
n.sons[a].sons[1], n.sons[a].sons[1])
|
n.sons[a].sons[1], n.sons[a].sons[1])
|
||||||
|
m.firstMismatch.kind = kTypeMismatch
|
||||||
if arg == nil:
|
if arg == nil:
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.firstMismatch = a
|
|
||||||
return
|
return
|
||||||
checkConstraint(n.sons[a].sons[1])
|
checkConstraint(n.sons[a].sons[1])
|
||||||
if m.baseTypeMatch:
|
if m.baseTypeMatch:
|
||||||
|
|
@ -2391,6 +2404,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
else:
|
else:
|
||||||
addSon(m.call, copyTree(n.sons[a]))
|
addSon(m.call, copyTree(n.sons[a]))
|
||||||
elif formal != nil and formal.typ.kind == tyVarargs:
|
elif formal != nil and formal.typ.kind == tyVarargs:
|
||||||
|
m.firstMismatch.kind = kTypeMismatch
|
||||||
# beware of the side-effects in 'prepareOperand'! So only do it for
|
# beware of the side-effects in 'prepareOperand'! So only do it for
|
||||||
# varargs matching. See tests/metatype/tstatic_overloading.
|
# varargs matching. See tests/metatype/tstatic_overloading.
|
||||||
m.baseTypeMatch = false
|
m.baseTypeMatch = false
|
||||||
|
|
@ -2407,6 +2421,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
m.firstMismatch.kind = kExtraArg
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
|
|
@ -2414,7 +2429,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
internalError(c.config, n.sons[a].info, "matches")
|
internalError(c.config, n.sons[a].info, "matches")
|
||||||
return
|
return
|
||||||
formal = m.callee.n.sons[f].sym
|
formal = m.callee.n.sons[f].sym
|
||||||
|
m.firstMismatch.kind = kTypeMismatch
|
||||||
if containsOrIncl(marker, formal.position) and container.isNil:
|
if containsOrIncl(marker, formal.position) and container.isNil:
|
||||||
|
m.firstMismatch.kind = kAlreadyGiven
|
||||||
# already in namedParams: (see above remark)
|
# already in namedParams: (see above remark)
|
||||||
when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
|
when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
|
|
@ -2435,7 +2452,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
|
||||||
n.sons[a], nOrig.sons[a])
|
n.sons[a], nOrig.sons[a])
|
||||||
if arg == nil:
|
if arg == nil:
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.firstMismatch = f
|
|
||||||
return
|
return
|
||||||
if m.baseTypeMatch:
|
if m.baseTypeMatch:
|
||||||
assert formal.typ.kind == tyVarargs
|
assert formal.typ.kind == tyVarargs
|
||||||
|
|
@ -2508,7 +2524,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||||
else:
|
else:
|
||||||
# no default value
|
# no default value
|
||||||
m.state = csNoMatch
|
m.state = csNoMatch
|
||||||
m.firstMismatch = f
|
m.firstMismatch.kind = kMissingParam
|
||||||
|
m.firstMismatch.formal = formal
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if formal.ast.kind == nkEmpty:
|
if formal.ast.kind == nkEmpty:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue