From 0a058a6b8f32749ebb19bfcd824b9f219d317f68 Mon Sep 17 00:00:00 2001 From: metagn Date: Fri, 18 Oct 2024 20:06:42 +0300 Subject: [PATCH] better errors for standalone explicit generic instantiations (#24276) refs #8064, refs #24010 Error messages for standalone explicit generic instantiations are revamped. Failing standalone explicit generic instantiations now only error after overloading has finished and resolved to the default `[]` magic (this means `[]` can now be overloaded for procs but this isn't necessarily intentional, in #24010 it was documented that it isn't possible). The error messages for failed instantiations are also no longer a simple `cannot instantiate: foo` message, instead they now give the same type mismatch error message as overloads with mismatching explicit generic parameters. This is now possible due to the changes in #24010 that delegate all explicit generic proc instantiations to overload resolution. Old code that worked around this is now removed. `maybeInstantiateGeneric` could maybe also be removed in favor of just `explicitGenericSym`, the `result == n` case is due to `explicitGenericInstError` which is only for niche cases. Also, to cause "ambiguous identifier" error messages when the explicit instantiation is a symchoice and the expression context doesn't allow symchoices, we semcheck the sym/symchoice created by `explicitGenericSym` with the given expression flags. #8064 isn't entirely fixed because the error message is still misleading for the original case which does `values[1]`, as a consequence of #12664. --- compiler/semcall.nim | 44 ++++++++++---------- compiler/semexprs.nim | 37 +++++++--------- compiler/semmagic.nim | 21 +++++++--- compiler/sigmatch.nim | 3 ++ tests/generics/tpointerprocs.nim | 17 +++++--- tests/overload/tambiguousexplicitgeneric.nim | 6 +++ tests/overload/texplicitgenericdiscard.nim | 7 ++++ 7 files changed, 81 insertions(+), 54 deletions(-) create mode 100644 tests/overload/tambiguousexplicitgeneric.nim create mode 100644 tests/overload/texplicitgenericdiscard.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 89d1b6a7b4cb..a195e9857f07 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -864,7 +864,7 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode = localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n)) result = n -proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = +proc explicitGenericSym(c: PContext, n: PNode, s: PSym, errors: var CandidateErrors, doError: bool): PNode = if s.kind in {skTemplate, skMacro}: internalError c.config, n.info, "cannot get explicitly instantiated symbol of " & (if s.kind == skTemplate: "template" else: "macro") @@ -874,6 +874,11 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode = if m.state != csMatch: # state is csMatch only if *all* generic params were matched, # including implicit parameters + if doError: + errors.add(CandidateError( + sym: s, + firstMismatch: m.firstMismatch, + diagnostics: m.diagnostics)) return nil var newInst = generateInstance(c, s, m.bindings, n.info) newInst.typ.flags.excl tfUnresolved @@ -897,42 +902,39 @@ proc setGenericParams(c: PContext, n, expectedParams: PNode) = else: n[i].typ() = e.typ.skipTypes({tyTypeDesc}) -proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = +proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym, doError: bool): PNode = assert n.kind == nkBracketExpr setGenericParams(c, n, s.ast[genericParamsPos]) var s = s var a = n[0] + var errors: CandidateErrors = @[] if a.kind == nkSym: # common case; check the only candidate has the right # number of generic type parameters: - if s.ast[genericParamsPos].safeLen != n.len-1: - let expected = s.ast[genericParamsPos].safeLen - localError(c.config, getCallLineInfo(n), errGenerated, "cannot instantiate: '" & renderTree(n) & - "'; got " & $(n.len-1) & " typeof(s) but expected " & $expected) - return n - result = explicitGenericSym(c, n, s) - if result == nil: result = explicitGenericInstError(c, n) + result = explicitGenericSym(c, n, s, errors, doError) + if doError and result == nil: + notFoundError(c, n, errors) elif a.kind in {nkClosedSymChoice, nkOpenSymChoice}: # choose the generic proc with the proper number of type parameters. - # XXX I think this could be improved by reusing sigmatch.paramTypesMatch. - # It's good enough for now. result = newNodeI(a.kind, getCallLineInfo(n)) for i in 0.. 1: + if n[1].kind == nkSym: n[1].sym + elif n[1].kind in nkSymChoices + {nkOpenSym} and n[1].len != 0: + n[1][0].sym + else: nil + else: nil + if s != nil and s.kind in routineKinds: + # this is a failed generic instantiation + # semSubscript should already error but this is better for cascading errors + result = explicitGenericInstError(c, n) + else: + bracketNotFoundError(c, x, flags) + result = errorNode(c, n) proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode = # rewrite `[]=`(a, i, x) back to ``a[i] = x``. diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index b76696230dc7..3538ea83cd20 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -200,6 +200,9 @@ proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) = elif tfImplicitTypeParam in paramSym.typ.flags: # not a mismatch, but can't create sym m.state = csEmpty + m.firstMismatch.kind = kMissingGenericParam + m.firstMismatch.arg = i + 1 + m.firstMismatch.formal = paramSym return else: m.state = csNoMatch diff --git a/tests/generics/tpointerprocs.nim b/tests/generics/tpointerprocs.nim index 2bcaf15b3686..29c4f2954f58 100644 --- a/tests/generics/tpointerprocs.nim +++ b/tests/generics/tpointerprocs.nim @@ -2,10 +2,17 @@ discard """ cmd: "nim check $options --hints:off $file" action: "reject" nimout:''' -tpointerprocs.nim(15, 11) Error: 'foo' doesn't have a concrete type, due to unspecified generic parameters. -tpointerprocs.nim(27, 11) Error: cannot instantiate: 'foo[int]'; got 1 typeof(s) but expected 2 -tpointerprocs.nim(27, 14) Error: expression 'foo[int]' has no type (or is ambiguous) -tpointerprocs.nim(28, 11) Error: expression 'bar' has no type (or is ambiguous) +tpointerprocs.nim(22, 11) Error: 'foo' doesn't have a concrete type, due to unspecified generic parameters. +tpointerprocs.nim(34, 14) Error: type mismatch: got +but expected one of: +proc foo(x: int | float; y: int or string): float + first type mismatch at position: 2 in generic parameters + missing generic parameter: y:type + +expression: foo[int] +tpointerprocs.nim(34, 14) Error: cannot instantiate: 'foo[int]' +tpointerprocs.nim(34, 14) Error: expression 'foo[int]' has no type (or is ambiguous) +tpointerprocs.nim(35, 11) Error: expression 'bar' has no type (or is ambiguous) ''' """ @@ -25,4 +32,4 @@ block: proc foo(x: int | float, y: int or string): float = result = 1.0 let bar = foo[int] - baz = bar \ No newline at end of file + baz = bar diff --git a/tests/overload/tambiguousexplicitgeneric.nim b/tests/overload/tambiguousexplicitgeneric.nim new file mode 100644 index 000000000000..cc6e3dbe4055 --- /dev/null +++ b/tests/overload/tambiguousexplicitgeneric.nim @@ -0,0 +1,6 @@ +# related to issue #8064 + +import tables + +let x = values[int] #[tt.Error + ^ ambiguous identifier: 'values' -- use one of the following:]# diff --git a/tests/overload/texplicitgenericdiscard.nim b/tests/overload/texplicitgenericdiscard.nim new file mode 100644 index 000000000000..1a207849b693 --- /dev/null +++ b/tests/overload/texplicitgenericdiscard.nim @@ -0,0 +1,7 @@ +# issue #8064 + +import tables + +values[int] #[tt.Error +^ ambiguous identifier: 'values' -- use one of the following:]# +# this happens before discard check, so no discard error