summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormetagn <metagngn@gmail.com>2022-11-28 23:33:02 +0300
committerGitHub <noreply@github.com>2022-11-28 21:33:02 +0100
commit555c5ed1a73fa783ee5b7526cb7a3f773f3b7f33 (patch)
tree3c1f42ea72e0bee4ed3b855c7133adbb6b327570
parentf31dc63169b685320a8d06dc8f1b9eb5930b2a87 (diff)
downloadNim-555c5ed1a73fa783ee5b7526cb7a3f773f3b7f33.tar.gz
fix bugs with dot & call operators [backport] (#20931)
* better error messages for dot operators [backport]

fixes #13063

* also fixes #7777

* fix #6981 and #9831 too

* fix

* minor improvement

* sus test fixes

* make test multiplatform lol

* fix nimsuggest test, extra improvements
-rw-r--r--compiler/semcall.nim58
-rw-r--r--compiler/semexprs.nim9
-rw-r--r--nimsuggest/tests/tgeneric_highlight.nim2
-rw-r--r--tests/concepts/texplain.nim38
-rw-r--r--tests/specialops/terrmsgs.nim76
-rw-r--r--tests/specialops/tnewseq.nim22
6 files changed, 156 insertions, 49 deletions
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index fa5cee69a..31b490781 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -287,8 +287,23 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
     # fail fast:
     globalError(c.config, n.info, "type mismatch")
     return
+  # see getMsgDiagnostic:
+  if nfExplicitCall notin n.flags and {nfDotField, nfDotSetter} * n.flags != {}:
+    let ident = considerQuotedIdent(c, n[0], n).s
+    let sym = n[1].typ.typSym
+    var typeHint = ""
+    if sym == nil:
+      discard
+    else:
+      typeHint = " for type " & getProcHeader(c.config, sym)
+    localError(c.config, n.info, errUndeclaredField % ident & typeHint)
+    return
   if errors.len == 0:
-    localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
+    if n[0].kind in nkIdentKinds:
+      let ident = considerQuotedIdent(c, n[0], n).s
+      localError(c.config, n.info, errUndeclaredRoutine % ident)
+    else: 
+      localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
     return
 
   let (prefer, candidates) = presentFailedCandidates(c, n, errors)
@@ -330,7 +345,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
       sym = nextOverloadIter(o, c, f)
 
   let ident = considerQuotedIdent(c, f, n).s
-  if {nfDotField, nfExplicitCall} * n.flags == {nfDotField}:
+  if nfExplicitCall notin n.flags and {nfDotField, nfDotSetter} * n.flags != {}:
     let sym = n[1].typ.typSym
     var typeHint = ""
     if sym == nil:
@@ -363,11 +378,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
   else:
     initialBinding = nil
 
-  template pickBest(headSymbol) =
+  pickBestCandidate(c, f, n, orig, initialBinding,
+                    filter, result, alt, errors, efExplain in flags,
+                    errorsEnabled, flags)
+
+  var dummyErrors: CandidateErrors
+  template pickSpecialOp(headSymbol) =
     pickBestCandidate(c, headSymbol, n, orig, initialBinding,
-                      filter, result, alt, errors, efExplain in flags,
-                      errorsEnabled, flags)
-  pickBest(f)
+                      filter, result, alt, dummyErrors, efExplain in flags,
+                      false, flags)
 
   let overloadsState = result.state
   if overloadsState != csMatch:
@@ -383,7 +402,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
         let op = newIdentNode(getIdent(c.cache, x), n.info)
         n[0] = op
         orig[0] = op
-        pickBest(op)
+        pickSpecialOp(op)
 
       if nfExplicitCall in n.flags:
         tryOp ".()"
@@ -397,26 +416,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       let callOp = newIdentNode(getIdent(c.cache, ".="), n.info)
       n.sons[0..1] = [callOp, n[1], calleeName]
       orig.sons[0..1] = [callOp, orig[1], calleeName]
-      pickBest(callOp)
+      pickSpecialOp(callOp)
 
     if overloadsState == csEmpty and result.state == csEmpty:
       if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
-        template impl() =
-          result.state = csNoMatch
-          if efNoDiagnostics in flags:
-            return
-          # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
-          localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
-        if n[0].kind == nkIdent and n[0].ident.s == ".=" and n[2].kind == nkIdent:
-          let sym = n[1].typ.sym
-          if sym == nil:
-            impl()
-          else:
-            let field = n[2].ident.s
-            let msg = errUndeclaredField % field & " for type " & getProcHeader(c.config, sym)
-            localError(c.config, orig[2].info, msg)
-        else:
-          impl()
+        result.state = csNoMatch
+        if efNoDiagnostics in flags:
+          return
+        # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
+        localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
@@ -619,7 +627,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
 proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   assert n.kind == nkBracketExpr
   for i in 1..<n.len:
-    let e = semExpr(c, n[i])
+    let e = semExprWithType(c, n[i])
     if e.typ == nil:
       n[i].typ = errorType(c)
     else:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 265552e4d..d6745e8e4 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -535,7 +535,7 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
     result = newNodeI(nkCall, n.info)
     result.add newIdentNode(par, n.info)
     for i in 0..<n.len: result.add n[i]
-    result = semExpr(c, result)
+    result = semExpr(c, result, flags = {efNoUndeclared})
 
 proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
   case n.kind
@@ -993,6 +993,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
       if s != nil:
         setGenericParams(c, n[0])
         return semDirectOp(c, n, flags, expectedType)
+    elif isSymChoice(n[0]):
+      # overloaded generic procs e.g. newSeq[int] can end up here
+      return semDirectOp(c, n, flags, expectedType)
 
   var t: PType = nil
   if n[0].typ != nil:
@@ -1042,10 +1045,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
       instGenericConvertersSons(c, result, m)
 
   else:
-    result = overloadedCallOpr(c, n)
+    result = overloadedCallOpr(c, n) # this uses efNoUndeclared
     # Now that nkSym does not imply an iteration over the proc/iterator space,
     # the old ``prc`` (which is likely an nkIdent) has to be restored:
-    if result == nil:
+    if result == nil or result.kind == nkEmpty:
       # XXX: hmm, what kind of symbols will end up here?
       # do we really need to try the overload resolution?
       n[0] = prc
diff --git a/nimsuggest/tests/tgeneric_highlight.nim b/nimsuggest/tests/tgeneric_highlight.nim
index 334323613..f351ab705 100644
--- a/nimsuggest/tests/tgeneric_highlight.nim
+++ b/nimsuggest/tests/tgeneric_highlight.nim
@@ -8,12 +8,10 @@ $nimsuggest --tester $file
 highlight;;skType;;1;;7;;3
 highlight;;skProc;;1;;0;;6
 highlight;;skProc;;1;;0;;6
-highlight;;skType;;1;;7;;3
 highlight;;skProc;;1;;0;;6
 highlight;;skType;;2;;14;;3
 highlight;;skProc;;2;;7;;6
 highlight;;skProc;;2;;7;;6
-highlight;;skType;;2;;14;;3
 highlight;;skProc;;2;;7;;6
 highlight;;skTemplate;;3;;0;;8
 highlight;;skType;;3;;9;;3
diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim
index 49eb8eb6b..8cf04ae82 100644
--- a/tests/concepts/texplain.nim
+++ b/tests/concepts/texplain.nim
@@ -13,14 +13,8 @@ proc e(o: ExplainedConcept): int
   required type for o: ExplainedConcept
   but expression '10' is of type: int literal(10)
 texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(128, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(128, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(128, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(129, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(129, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(129, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 
 texplain.nim(168, 10) Hint: Non-matching candidates for e(10)
@@ -29,14 +23,8 @@ proc e(o: ExplainedConcept): int
   required type for o: ExplainedConcept
   but expression '10' is of type: int literal(10)
 texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(128, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(128, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(128, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(129, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(129, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(129, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 
 texplain.nim(172, 20) Error: type mismatch: got <NonMatchingType>
@@ -88,14 +76,8 @@ proc f(o: NestedConcept)
   required type for o: NestedConcept
   but expression 'y' is of type: MatchingType
 texplain.nim(132, 6) RegularConcept: undeclared field: 'foo'
-texplain.nim(132, 6) RegularConcept: undeclared field: '.'
-texplain.nim(132, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(132, 6) RegularConcept: expression '' has no type (or is ambiguous)
 texplain.nim(132, 5) RegularConcept: concept predicate failed
 texplain.nim(133, 6) RegularConcept: undeclared field: 'bar'
-texplain.nim(133, 6) RegularConcept: undeclared field: '.'
-texplain.nim(133, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(133, 6) RegularConcept: expression '' has no type (or is ambiguous)
 texplain.nim(132, 5) RegularConcept: concept predicate failed
 texplain.nim(136, 5) NestedConcept: concept predicate failed
 
@@ -121,7 +103,25 @@ expression: f(y)'''
 
 
 
-# line 120 HERE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# line 124 HERE
 
 type
   ExplainedConcept {.explain.} = concept o
diff --git a/tests/specialops/terrmsgs.nim b/tests/specialops/terrmsgs.nim
new file mode 100644
index 000000000..d1a790e54
--- /dev/null
+++ b/tests/specialops/terrmsgs.nim
@@ -0,0 +1,76 @@
+discard """
+action: reject
+cmd: '''nim check $options $file'''
+matrix: "; -d:testWithout"
+"""
+
+when not defined(testWithout): # test for same errors before and after
+  {.experimental: "dotOperators".}
+  {.experimental: "callOperator".}
+
+# issue #13063
+
+block:
+  type Foo = object
+  type Bar = object
+    x1: int
+  var b: Bar
+  block:
+    template `.`(a: Foo, b: untyped): untyped = 123
+    echo b.x #[tt.Error
+          ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+  block:
+    template `.()`(a: Foo, b: untyped): untyped = 123
+    echo b.x() #[tt.Error
+          ^ attempting to call undeclared routine: 'x']#
+  block:
+    template `.=`(a: Foo, b: untyped, c: untyped) = b = c
+    b.x = 123 #[tt.Error
+        ^ undeclared field: 'x=' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+    # yeah it says x= but does it matter in practice
+  block:
+    template `()`(a: Foo, b: untyped, c: untyped) = echo "something"
+
+    # completely undeclared::
+    xyz(123) #[tt.Error
+    ^ undeclared identifier: 'xyz']#
+
+    # already declared routine:
+    min(123) #[tt.Error
+       ^ type mismatch: got <int literal(123)>]#
+
+    # non-routine type shows `()` overloads:
+    b(123) #[tt.Error
+     ^ attempting to call routine: 'b']#
+
+    echo b.x #[tt.Error
+          ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+    echo b.x() #[tt.Error
+          ^ attempting to call undeclared routine: 'x']#
+
+# issue #7777
+
+import macros
+
+block:
+  type TestType = object
+    private_field: string
+
+  when false:
+    template getField(obj, field: untyped): untyped = obj.field
+
+  macro `.`(obj: TestType, field: untyped): untyped =
+    let private = newIdentNode("private_" & $field)
+    result = quote do:
+      `obj`.getField(`private`) #[tt.Error
+           ^ attempting to call undeclared routine: 'getField']#
+
+  var tt: TestType
+  discard tt.field
+
+block: # related to issue #6981
+  proc `()`(a:string, b:string):string = a & b
+  proc mewSeq[T](a,b:int)=discard
+  proc mewSeq[T](c:int)= discard
+  mewSeq[int]() #[tt.Error
+             ^ type mismatch: got <>]#
diff --git a/tests/specialops/tnewseq.nim b/tests/specialops/tnewseq.nim
new file mode 100644
index 000000000..970a5a8c2
--- /dev/null
+++ b/tests/specialops/tnewseq.nim
@@ -0,0 +1,22 @@
+# issue #6981
+
+import std/assertions
+
+{.experimental: "callOperator".}
+
+block: # issue #6981
+  proc `()`(a:string, b:string):string = a & b
+
+  var s = newSeq[int](3)
+
+  doAssert s == @[0, 0, 0]
+
+block: # generalized example from #6981
+  proc mewSeq[T](a: int)=discard
+  proc mewSeq[T]()= discard
+  mewSeq[int]()
+
+block: # issue #9831
+  type Foo = object
+  proc `()`(foo: Foo) = discard
+  let x = newSeq[int]()