From 67ad1ae1598b08039c971812dc172dd48624b7b0 Mon Sep 17 00:00:00 2001 From: metagn Date: Wed, 6 Nov 2024 12:54:03 +0300 Subject: [PATCH] fix standalone explicit generic procs with unresolved arguments (#24404) 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. --- compiler/semcall.nim | 18 ++++++++++++++++-- compiler/semexprs.nim | 4 +++- tests/proc/texplicitgenerics.nim | 8 ++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index fb2f9e97a78e..d0ccb17b1fa1 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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)) @@ -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 diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6cc4afd6e008..e4c585e0130d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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: diff --git a/tests/proc/texplicitgenerics.nim b/tests/proc/texplicitgenerics.nim index 833d77b3b790..131a31458d5c 100644 --- a/tests/proc/texplicitgenerics.nim +++ b/tests/proc/texplicitgenerics.nim @@ -53,3 +53,11 @@ block: # issue #21346 b1(false) # Error: cannot instantiate K; got: but expected: 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"]