diff options
author | metagn <metagngn@gmail.com> | 2022-11-28 23:33:02 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-28 21:33:02 +0100 |
commit | 555c5ed1a73fa783ee5b7526cb7a3f773f3b7f33 (patch) | |
tree | 3c1f42ea72e0bee4ed3b855c7133adbb6b327570 | |
parent | f31dc63169b685320a8d06dc8f1b9eb5930b2a87 (diff) | |
download | Nim-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.nim | 58 | ||||
-rw-r--r-- | compiler/semexprs.nim | 9 | ||||
-rw-r--r-- | nimsuggest/tests/tgeneric_highlight.nim | 2 | ||||
-rw-r--r-- | tests/concepts/texplain.nim | 38 | ||||
-rw-r--r-- | tests/specialops/terrmsgs.nim | 76 | ||||
-rw-r--r-- | tests/specialops/tnewseq.nim | 22 |
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]() |