Skip to content

Commit

Permalink
fix standalone explicit generic procs with unresolved arguments (#24404)
Browse files Browse the repository at this point in the history
fixes issue described in https://forum.nim-lang.org/t/12579

In #24065 explicit generic parameter matching was made to fail matches
on arguments with unresolved types in generic contexts (the sigmatch
diff, following #24010), similar to what is done for regular calls since
#22029. However unlike regular calls, a failed match in a generic
context for a standalone explicit generic instantiation did not convert
the expression into one with `tyFromExpr` type, which means it would
error immediately given any unresolved parameter. This is now done to
fix the issue.

For explicit generic instantiations on single non-overloaded symbols, a
successful match is still instantiated. For multiple overloads (i.e.
symchoice), if any of the overloads fail the match, the entire
expression is considered untyped and any instantiations are not used, so
as to not void overloads that would match later. This means even
symchoices without unresolved arguments aren't instantiated, which may
be too restrictive, but it could also be too lenient and we might need
to make symchoice instantiations always untyped. The behavior for
symchoice is not sound anyway given it causes #9997 so this is something
to consider for a redesign.

Diff follows #24276.
  • Loading branch information
metagn authored Nov 6, 2024
1 parent cfd8f8b commit 67ad1ae
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 3 deletions.
18 changes: 16 additions & 2 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -973,8 +973,14 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
# common case; check the only candidate has the right
# number of generic type parameters:
result = explicitGenericSym(c, n, s, errors, doError)
if doError and result == nil:
notFoundError(c, n, errors)
if result == nil:
if c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif doError:
notFoundError(c, n, errors)
elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}:
# choose the generic proc with the proper number of type parameters.
result = newNodeI(a.kind, getCallLineInfo(n))
Expand All @@ -984,6 +990,14 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool)
skFunc, skIterator}:
let x = explicitGenericSym(c, n, candidate, errors, doError)
if x != nil: result.add(x)
elif c.inGenericContext > 0:
# same as in semOverloadedCall, make expression untyped,
# may have failed match due to unresolved types
# any failing match stops building the symchoice for correctness,
# can also make it untyped from the start
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
return
# get rid of nkClosedSymChoice if not ambiguous:
if result.len == 0:
result = nil
Expand Down
4 changes: 3 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1816,7 +1816,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags, afterOverloading = f
# type parameters: partial generic specialization
n[0] = semSymGenericInstantiation(c, n[0], s)
result = maybeInstantiateGeneric(c, n, s, doError = afterOverloading)
if result != nil:
if result != nil and
# leave untyped generic expression alone:
(result.typ == nil or result.typ.kind != tyFromExpr):
# check newly created sym/symchoice
result = semExpr(c, result, flags)
of skMacro, skTemplate:
Expand Down
8 changes: 8 additions & 0 deletions tests/proc/texplicitgenerics.nim
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,11 @@ block: # issue #21346
b1(false) # Error: cannot instantiate K; got: <T> but expected: <T>
b2(false) # Builds, on its own
b3(false)

block: # explicit generic with unresolved generic param, https://forum.nim-lang.org/t/12579
var s: seq[string] = @[]
proc MyMedian[T](A: var openArray[T],myCmp : proc(x,y:T):int {.nimcall.} = cmp[T]) : T =
if myCmp(A[0], A[1]) == 0: s.add("1")
var x = [1, 1]
discard MyMedian(x) # emits "1\n"
doAssert s == @["1"]

0 comments on commit 67ad1ae

Please sign in to comment.