mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-18 10:37:12 +00:00
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
(cherry picked from commit 555c5ed1a7)
This commit is contained in:
@@ -288,8 +288,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)
|
||||
@@ -331,7 +346,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:
|
||||
@@ -364,11 +379,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:
|
||||
@@ -400,7 +419,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 ".()"
|
||||
@@ -414,23 +433,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() =
|
||||
# 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:
|
||||
@@ -654,7 +665,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:
|
||||
|
||||
@@ -522,7 +522,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
|
||||
@@ -970,6 +970,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if s != nil:
|
||||
setGenericParams(c, n[0])
|
||||
return semDirectOp(c, n, flags)
|
||||
elif isSymChoice(n[0]):
|
||||
# overloaded generic procs e.g. newSeq[int] can end up here
|
||||
return semDirectOp(c, n, flags, expectedType)
|
||||
|
||||
let nOrig = n.copyTree
|
||||
semOpAux(c, n)
|
||||
@@ -1018,10 +1021,10 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
if n.len == 1: return semObjConstr(c, n, flags)
|
||||
return semConv(c, n)
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
76
tests/specialops/terrmsgs.nim
Normal file
76
tests/specialops/terrmsgs.nim
Normal file
@@ -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 <>]#
|
||||
22
tests/specialops/tnewseq.nim
Normal file
22
tests/specialops/tnewseq.nim
Normal file
@@ -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]()
|
||||
Reference in New Issue
Block a user