less verbose type mismatch messages (#21191)

* less verbose type mismatch messages

* Update compiler/types.nim

* fixes i386

* fixes i386
This commit is contained in:
ringabout 2022-12-29 03:54:15 +08:00 • committed by GitHub
commit 761c5a0830
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 27 deletions

View file

@ -135,6 +135,8 @@
- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. - The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection.
- - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
## Standard library additions and changes ## Standard library additions and changes
[//]: # "Changes:" [//]: # "Changes:"

View file

@ -231,6 +231,7 @@ type
## are not anymore. ## are not anymore.
laxEffects laxEffects
## Lax effects system prior to Nim 2.0. ## Lax effects system prior to Nim 2.0.
verboseTypeMismatch
SymbolFilesOption* = enum SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest

View file

@ -10,7 +10,8 @@
## This module implements semantic checking for calls. ## This module implements semantic checking for calls.
# included from sem.nim # included from sem.nim
from algorithm import sort from std/algorithm import sort
proc sameMethodDispatcher(a, b: PSym): bool = proc sameMethodDispatcher(a, b: PSym): bool =
result = false result = false
@ -192,7 +193,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
# argument in order to remove plenty of candidates. This is # argument in order to remove plenty of candidates. This is
# comparable to what C# does and C# is doing fine. # comparable to what C# does and C# is doing fine.
var filterOnlyFirst = false var filterOnlyFirst = false
if optShowAllMismatches notin c.config.globalOptions: if optShowAllMismatches notin c.config.globalOptions and verboseTypeMismatch in c.config.legacyFeatures:
for err in errors: for err in errors:
if err.firstMismatch.arg > 1: if err.firstMismatch.arg > 1:
filterOnlyFirst = true filterOnlyFirst = true
@ -208,6 +209,10 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
if filterOnlyFirst and err.firstMismatch.arg == 1: if filterOnlyFirst and err.firstMismatch.arg == 1:
inc skipped inc skipped
continue continue
if verboseTypeMismatch notin c.config.legacyFeatures:
candidates.add "[" & $err.firstMismatch.arg & "] "
if err.sym.kind in routineKinds and err.sym.ast != nil: if err.sym.kind in routineKinds and err.sym.ast != nil:
candidates.add(renderTree(err.sym.ast, candidates.add(renderTree(err.sym.ast,
{renderNoBody, renderNoComments, renderNoPragmas})) {renderNoBody, renderNoComments, renderNoPragmas}))
@ -217,7 +222,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
candidates.add("\n") candidates.add("\n")
let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
if n.len > 1: if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures:
candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg)
# candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging
case err.firstMismatch.kind case err.firstMismatch.kind
@ -274,11 +279,28 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
const const
errTypeMismatch = "type mismatch: got <" errTypeMismatch = "type mismatch: got <"
errButExpected = "but expected one of:" errButExpected = "but expected one of:"
errExpectedPosition = "Expected one of (first mismatch at position [#]):"
errUndeclaredField = "undeclared field: '$1'" errUndeclaredField = "undeclared field: '$1'"
errUndeclaredRoutine = "attempting to call undeclared routine: '$1'" errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
errBadRoutine = "attempting to call routine: '$1'$2" errBadRoutine = "attempting to call routine: '$1'$2"
errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3" errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3"
proc describeParamList(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
result = "Expression: " & $n
for i in startIdx..<n.len:
result.add "\n [" & $i & "] " & renderTree(n[i]) & ": "
result.add describeArg(c, n, i, startIdx, prefer)
result.add "\n"
template legacynotFoundError(c: PContext, n: PNode, errors: CandidateErrors) =
let (prefer, candidates) = presentFailedCandidates(c, n, errors)
var result = errTypeMismatch
result.add(describeArgs(c, n, 1, prefer))
result.add('>')
if candidates != "":
result.add("\n" & errButExpected & "\n" & candidates)
localError(c.config, n.info, result & "\nexpression: " & $n)
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
# Gives a detailed error message; this is separated from semOverloadedCall, # Gives a detailed error message; this is separated from semOverloadedCall,
# as semOverloadedCall is already pretty slow (and we need this information # as semOverloadedCall is already pretty slow (and we need this information
@ -306,13 +328,15 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree) localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
return return
let (prefer, candidates) = presentFailedCandidates(c, n, errors) if verboseTypeMismatch in c.config.legacyFeatures:
var result = errTypeMismatch legacynotFoundError(c, n, errors)
result.add(describeArgs(c, n, 1, prefer)) else:
result.add('>') let (prefer, candidates) = presentFailedCandidates(c, n, errors)
if candidates != "": var result = "type mismatch\n"
result.add("\n" & errButExpected & "\n" & candidates) result.add describeParamList(c, n, 1, prefer)
localError(c.config, n.info, result & "\nexpression: " & $n) if candidates != "":
result.add("\n" & errExpectedPosition & "\n" & candidates)
localError(c.config, n.info, result)
proc bracketNotFoundError(c: PContext; n: PNode) = proc bracketNotFoundError(c: PContext; n: PNode) =
var errors: CandidateErrors = @[] var errors: CandidateErrors = @[]

View file

@ -322,26 +322,32 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
else: else:
result = arg.typ.typeToString(prefer) result = arg.typ.typeToString(prefer)
template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName) =
var arg = n[i]
if n[i].kind == nkExprEqExpr:
result.add renderTree(n[i][0])
result.add ": "
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
# XXX we really need to 'tryExpr' here!
arg = c.semOperand(c, n[i][1])
n[i].typ = arg.typ
n[i][1] = arg
else:
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
nkOfBranch, nkElifBranch,
nkExceptBranch}:
arg = c.semOperand(c, n[i])
n[i] = arg
if arg.typ != nil and arg.typ.kind == tyError: return
result.add argTypeToString(arg, prefer)
proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string =
describeArgImpl(c, n, i, startIdx, prefer)
proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
result = "" result = ""
for i in startIdx..<n.len: for i in startIdx..<n.len:
var arg = n[i] describeArgImpl(c, n, i, startIdx, prefer)
if n[i].kind == nkExprEqExpr:
result.add renderTree(n[i][0])
result.add ": "
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
# XXX we really need to 'tryExpr' here!
arg = c.semOperand(c, n[i][1])
n[i].typ = arg.typ
n[i][1] = arg
else:
if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
nkOfBranch, nkElifBranch,
nkExceptBranch}:
arg = c.semOperand(c, n[i])
n[i] = arg
if arg.typ != nil and arg.typ.kind == tyError: return
result.add argTypeToString(arg, prefer)
if i != n.len - 1: result.add ", " if i != n.len - 1: result.add ", "
proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType = proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =

View file

@ -44,3 +44,4 @@ when defined(windows):
switch("tlsEmulation", "off") switch("tlsEmulation", "off")
switch("warningAserror", "UnnamedBreak") switch("warningAserror", "UnnamedBreak")
switch("legacy", "verboseTypeMismatch")

View file

@ -0,0 +1,23 @@
discard """
cmd: "nim c --hints:off --skipParentCfg $file"
errormsg: "type mismatch"
nimout: '''
tconcisetypemismatch.nim(23, 47) Error: type mismatch
Expression: int(inNanoseconds(t2 - t1)) / 100.5
[1] int(inNanoseconds(t2 - t1)): int
[2] 100.5: float64
Expected one of (first mismatch at position [#]):
[1] proc `/`(x, y: float): float
[1] proc `/`(x, y: float32): float32
[2] proc `/`(x, y: int): float
'''
"""
import std/monotimes
from times import inNanoseconds
let t1 = getMonotime()
let result = 1 + 2
let t2 = getMonotime()
echo "Elapsed: ", (t2 - t1).inNanoseconds.int / 100.5

View file

@ -0,0 +1,21 @@
switch("path", "$lib/../testament/lib")
# so we can `import stdtest/foo` inside tests
# Using $lib/../ instead of $nim/ so you can use a different nim to run tests
# during local testing, e.g. nim --lib:lib.
## prevent common user config settings to interfere with testament expectations
## Indifidual tests can override this if needed to test for these options.
switch("colors", "off")
switch("excessiveStackTrace", "off")
when (NimMajor, NimMinor, NimPatch) >= (1,5,1):
# to make it easier to test against older nim versions, (best effort only)
switch("filenames", "legacyRelProj")
switch("spellSuggest", "0")
# for std/unittest
switch("define", "nimUnittestOutputLevel:PRINT_FAILURES")
switch("define", "nimUnittestColor:off")
hint("Processing", off)