summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelogs/changelog_2_0_0.md2
-rw-r--r--compiler/options.nim1
-rw-r--r--compiler/semcall.nim44
-rw-r--r--compiler/sigmatch.nim40
-rw-r--r--tests/config.nims1
-rw-r--r--tests/errmsgs/tconcisetypemismatch.nim23
-rw-r--r--tests/errmsgs/tconcisetypemismatch.nims21
7 files changed, 105 insertions, 27 deletions
diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md
index 614d97d85..29d906d62 100644
--- a/changelogs/changelog_2_0_0.md
+++ b/changelogs/changelog_2_0_0.md
@@ -135,6 +135,8 @@
 
 - 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
 
 [//]: # "Changes:"
diff --git a/compiler/options.nim b/compiler/options.nim
index 7e4f6898f..ded084ae4 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -231,6 +231,7 @@ type
       ## are not anymore.
     laxEffects
       ## Lax effects system prior to Nim 2.0.
+    verboseTypeMismatch
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 31b490781..2147a9645 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -10,7 +10,8 @@
 ## This module implements semantic checking for calls.
 # included from sem.nim
 
-from algorithm import sort
+from std/algorithm import sort
+
 
 proc sameMethodDispatcher(a, b: PSym): bool =
   result = false
@@ -192,7 +193,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
   # argument in order to remove plenty of candidates. This is
   # comparable to what C# does and C# is doing fine.
   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:
       if err.firstMismatch.arg > 1:
         filterOnlyFirst = true
@@ -208,6 +209,10 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
     if filterOnlyFirst and err.firstMismatch.arg == 1:
       inc skipped
       continue
+
+    if verboseTypeMismatch notin c.config.legacyFeatures:
+      candidates.add "[" & $err.firstMismatch.arg & "] "
+
     if err.sym.kind in routineKinds and err.sym.ast != nil:
       candidates.add(renderTree(err.sym.ast,
             {renderNoBody, renderNoComments, renderNoPragmas}))
@@ -217,7 +222,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
     candidates.add("\n")
     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: ""
-    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 "\n  reason: " & $err.firstMismatch.kind # for debugging
       case err.firstMismatch.kind
@@ -274,11 +279,28 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
 const
   errTypeMismatch = "type mismatch: got <"
   errButExpected = "but expected one of:"
+  errExpectedPosition = "Expected one of (first mismatch at position [#]):"
   errUndeclaredField = "undeclared field: '$1'"
   errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
   errBadRoutine = "attempting to call routine: '$1'$2"
   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) =
   # Gives a detailed error message; this is separated from semOverloadedCall,
   # 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)
     return
 
-  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)
+  if verboseTypeMismatch in c.config.legacyFeatures:
+    legacynotFoundError(c, n, errors)
+  else:
+    let (prefer, candidates) = presentFailedCandidates(c, n, errors)
+    var result = "type mismatch\n"
+    result.add describeParamList(c, n, 1, prefer)
+    if candidates != "":
+      result.add("\n" & errExpectedPosition & "\n" & candidates)
+    localError(c.config, n.info, result)
 
 proc bracketNotFoundError(c: PContext; n: PNode) =
   var errors: CandidateErrors = @[]
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 68068cd26..75e3e5428 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -322,26 +322,32 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
   else:
     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 =
   result = ""
   for i in startIdx..<n.len:
-    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)
+    describeArgImpl(c, n, i, startIdx, prefer)
     if i != n.len - 1: result.add ", "
 
 proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
diff --git a/tests/config.nims b/tests/config.nims
index 421cabbf3..4bcd67143 100644
--- a/tests/config.nims
+++ b/tests/config.nims
@@ -44,3 +44,4 @@ when defined(windows):
   switch("tlsEmulation", "off")
 
 switch("warningAserror", "UnnamedBreak")
+switch("legacy", "verboseTypeMismatch")
diff --git a/tests/errmsgs/tconcisetypemismatch.nim b/tests/errmsgs/tconcisetypemismatch.nim
new file mode 100644
index 000000000..4227644ce
--- /dev/null
+++ b/tests/errmsgs/tconcisetypemismatch.nim
@@ -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
\ No newline at end of file
diff --git a/tests/errmsgs/tconcisetypemismatch.nims b/tests/errmsgs/tconcisetypemismatch.nims
new file mode 100644
index 000000000..e9dce8147
--- /dev/null
+++ b/tests/errmsgs/tconcisetypemismatch.nims
@@ -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)