Skip to content

fix #24912 #24913

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 17 commits into from
18 changes: 16 additions & 2 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -614,11 +614,19 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
if c.inGenericContext > 0 and nfExprCall in n.flags:
# untyped expression calls end up here, see #24099
return
if efPreferNilResult in flags:
result.state = csEmpty
#result.call = nil
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:
if efPreferNilResult in flags:
result.state = csEmpty
#result.call = nil
return
localError(c.config, n.info, "expression '$1' cannot be called" %
renderTree(n, {renderNoComments}))
else:
Expand All @@ -629,6 +637,10 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
return
if alt.state == csMatch and cmpCandidates(result, alt) == 0 and
not sameMethodDispatcher(result.calleeSym, alt.calleeSym):
if efPreferNilResult in flags:
result.state = csEmpty
#result.call = nil
return
internalAssert c.config, result.state == csMatch
#writeMatches(result)
#writeMatches(alt)
Expand Down Expand Up @@ -909,13 +921,15 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
if c.inGenericContext > 0 and c.matchedConcept == nil:
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif efExplain notin flags:
elif {efPreferNilResult, efExplain} * flags == {}:

# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
elif efNoUndeclared notin flags:
result = nil
notFoundError(c, n, errors)
if efPreferNilResult notin flags:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe reuse efNoUndeclared instead?

Copy link
Contributor Author

@Graveflo Graveflo May 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure about this one either. Another option might be to pass through errorsEnabled as it is in resolveOverloads and see if disabling the errors with this flag has bad side effects, but that doesn't sound great either. I'll use efNoUndeclared instead if I end up sticking with this.
edit: yea not sure how I missed that one

notFoundError(c, n, errors)
else:
result = nil

Expand Down
68 changes: 44 additions & 24 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -994,18 +994,18 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
let callee = result[0].sym
case callee.kind
of skMacro, skTemplate: discard
else:
if callee.kind == skIterator and callee.id == c.p.owner.id and
not isClosureIterator(c.p.owner.typ):
of skIterator:
if callee.id == c.p.owner.id and not isClosureIterator(c.p.owner.typ):
localError(c.config, n.info, errRecursiveDependencyIteratorX % callee.name.s)
# error correction, prevents endless for loop elimination in transf.
# See bug #2051:
result[0] = newSymNode(errorSym(c, n))
elif callee.kind == skIterator:
if efWantIterable in flags:
let typ = newTypeS(tyIterable, c)
rawAddSon(typ, result.typ)
result.typ() = typ
elif efWantIterable in flags:
let typ = newTypeS(tyIterable, c)
rawAddSon(typ, result.typ)
result.typ() = typ
else:
discard

proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
t: PType): TCandidate =
Expand Down Expand Up @@ -1104,15 +1104,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
elif isSymChoice(n[0]) and nfDotField notin n.flags:
# 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:
t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
if t != nil and t.kind == tyTypeDesc:
if n.len == 1: return semObjConstr(c, n, flags, expectedType)
return semConv(c, n, flags)

let nOrig = n.copyTree

var nOrig = n.copyTree
semOpAux(c, n)
if t != nil and t.kind == tyProc:
# This is a proc variable, apply normal overload resolution
Expand Down Expand Up @@ -1154,21 +1150,45 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType

else:
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 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
nOrig[0] = prc
n.flags.incl nfExprCall
result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
if result == nil: return errorNode(c, n)
var tcall = n.copyTree
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a helper proc here and document what the heck is actually does and why it needs to do it. The compiler code is already incomprehensible with all these arbitrary transformations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled out the part that is new. The rest is stuff that got re-positioned.

if n[0].kind == nkDotExpr and n[0].typ.kind notin {tyProc, tyGenericInst, tyOwned}:
tcall = n[0]
tcall.transitionSonsKind(nkCall)
for i in 1..<n.len: tcall.add n[i]
let tmp = tcall[1]
tcall[1] = tcall[0]
tcall[0] = tmp
tcall.typ() = nil
nOrig = tcall.copyTree
else:
# 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:
tcall[0] = prc
nOrig[0] = prc
tcall.flags.incl nfExprCall
let canspec = t != nil and t.kind in {tyFromExpr, tyTypeDesc}
var fflags = flags
if canspec:
fflags.incl efPreferNilResult
result = semOverloadedCallAnalyseEffects(c, tcall, nOrig, fflags)
if result == nil:
if canspec:
if n.len == 2:
return semConv(c, n, flags)
elif n.len == 1:
return semObjConstr(c, n, flags, expectedType)
elif result.kind notin nkCallKinds:
# the semExpr() in overloadedCallOpr can even break this condition!
# See bug #904 of how to trigger it:
return result
#result = afterCallActions(c, result, nOrig, flags)
if result == nil:
return errorNode(c, n)
if result.typ != nil and result.typ.kind == tyFromExpr and t != nil and t.kind == tyTypeDesc:
# this is wrong but tyFromExpr doesn't always seem to be a valid result
# anecdotally, this is what the compiler is trying to do but I think
# tyFromExpr needs to be handled elsewhere
return semObjConstr(c, n, flags, expectedType)
if result[0].kind == nkSym:
result = afterCallActions(c, result, nOrig, flags, expectedType)
else:
Expand Down
63 changes: 63 additions & 0 deletions tests/exprs/tdots.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
discard """
action: run
"""

#24913
block:
type
A = object
b: int
B = object
b: proc(x: float): int

proc b(T: typedesc[A]; s: float): int = 5
proc b(T: typedesc[B]; s: float): int = 7
proc testme(x: float): int = 3
doAssert A.b(1.0) == 5
doAssert B.b(1.0) == 7
doAssert B(b: testme).b(1.0) == 3

block:
type
Proc[T] = proc(text: T): int {.closure.}

Rule[T] = object
p: Proc[T]

proc p(x: Rule[int]; y: float): int = 5
proc sp(y: int): int = 3

proc spring[T](rule: Rule[T]) =
let p = proc (text: T) =
doAssert rule.p(text) == 3
p(default(T))

Rule[int](p: sp).spring()

block:
type
A = object

proc new(T: type A): ref A =
let c = (ref T)()
c

proc p[K](rng=A.new()) =
discard new(A)

p[int]()

# cant be in a block
type
Cont[T] = ref RootObj
Rule[T] = object
p: Cont[T]

proc p(x: Rule[int]; y: int): int = 5

proc spring[T](rule: Rule[T]) =
let p = proc (x: T) =
doAssert rule.p(x) == 5
p(default(T))

Rule[int]().spring()