From 4a548deb0886ba7ae7b21a0134ce5becfac7ea5e Mon Sep 17 00:00:00 2001 From: metagn Date: Fri, 6 Sep 2024 12:25:51 +0300 Subject: [PATCH] proper errors for subscript overloads (#24068) The magic `mArrGet`/`mArrPut` subscript overloads always match, so if a subscript doesn't match any other subscript overloads and isn't a regular language-handled subscript, it creates a fake overload mismatch error in `semArrGet` that doesn't have any information (gives stuff like "first mismatch at index: 0" for every single mismatch). Instead of generating the fake mismatches, we only generate the fake mismatch for `mArrGet`/`mArrPut`, and process every overload except them as a real call and get the errors from there. --- compiler/semcall.nim | 50 +++++++++++------ compiler/semexprs.nim | 2 +- compiler/semmagic.nim | 2 +- tests/errmsgs/t10735.nim | 60 ++++++++++++++------- tests/errmsgs/t22753.nim | 45 +++++++++++----- tests/errmsgs/tsubscriptmismatch.nim | 11 ++++ tests/errmsgs/tsubscriptmismatch_legacy.nim | 10 ++++ 7 files changed, 128 insertions(+), 52 deletions(-) create mode 100644 tests/errmsgs/tsubscriptmismatch.nim create mode 100644 tests/errmsgs/tsubscriptmismatch_legacy.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5fb61d5375265..06dd09b69f113 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -458,23 +458,6 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = result.add("\n" & errExpectedPosition & "\n" & candidates) localError(c.config, n.info, result) -proc bracketNotFoundError(c: PContext; n: PNode) = - var errors: CandidateErrors = @[] - var o: TOverloadIter = default(TOverloadIter) - let headSymbol = n[0] - var symx = initOverloadIter(o, c, headSymbol) - while symx != nil: - if symx.kind in routineKinds: - errors.add(CandidateError(sym: symx, - firstMismatch: MismatchInfo(), - diagnostics: @[], - enabled: false)) - symx = nextOverloadIter(o, c, headSymbol) - if errors.len == 0: - localError(c.config, n.info, "could not resolve: " & $n) - else: - notFoundError(c, n, errors) - proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = result = "" if c.compilesContextId > 0: @@ -603,6 +586,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode, getProcHeader(c.config, alt.calleeSym), args]) +proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) = + var errors: CandidateErrors = @[] + let headSymbol = n[0] + block: + # we build a closed symchoice of all `[]` overloads for their errors, + # except add a custom error for the magics which always match + var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c)) + var o: TOverloadIter = default(TOverloadIter) + var symx = initOverloadIter(o, c, headSymbol) + while symx != nil: + if symx.kind in routineKinds: + if symx.magic in {mArrGet, mArrPut}: + errors.add(CandidateError(sym: symx, + firstMismatch: MismatchInfo(), + diagnostics: @[], + enabled: false)) + else: + choice.add newSymNode(symx, headSymbol.info) + symx = nextOverloadIter(o, c, headSymbol) + n[0] = choice + # copied from semOverloadedCallAnalyzeEffects, might be overkill: + const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate} + let filter = + if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: + baseFilter + {skIterator} + else: baseFilter + # this will add the errors: + var r = resolveOverloads(c, n, n, filter, flags, errors, true) + if errors.len == 0: + localError(c.config, n.info, "could not resolve: " & $n) + else: + notFoundError(c, n, errors) + proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) = let a = if a.kind == nkHiddenDeref: a[0] else: a if a.kind == nkHiddenCallConv and a[0].kind == nkSym: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 39121a671adb1..9b255bdf4a600 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1993,7 +1993,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]=")) result.add(n[1]) if mode == noOverloadedSubscript: - bracketNotFoundError(c, result) + bracketNotFoundError(c, result, {}) return errorNode(c, n) else: result = semExprNoType(c, result) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 1d97f192653d3..524c9a0abd4fa 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -68,7 +68,7 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode = if result.isNil: let x = copyTree(n) x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info) - bracketNotFoundError(c, x) + bracketNotFoundError(c, x, flags) #localError(c.config, n.info, "could not resolve: " & $n) result = errorNode(c, n) diff --git a/tests/errmsgs/t10735.nim b/tests/errmsgs/t10735.nim index f480d35ac8dd8..a39cd196ec6fd 100644 --- a/tests/errmsgs/t10735.nim +++ b/tests/errmsgs/t10735.nim @@ -2,40 +2,62 @@ discard """ cmd: "nim check $file" errormsg: "illformed AST: case buf[pos]" nimout: ''' -t10735.nim(43, 5) Error: 'let' symbol requires an initialization -t10735.nim(44, 10) Error: undeclared identifier: 'pos' -t10735.nim(44, 10) Error: expression 'pos' has no type (or is ambiguous) -t10735.nim(44, 10) Error: expression 'pos' has no type (or is ambiguous) -t10735.nim(44, 9) Error: type mismatch: got +t10735.nim(65, 5) Error: 'let' symbol requires an initialization +t10735.nim(66, 10) Error: undeclared identifier: 'pos' +t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous) +t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous) +t10735.nim(66, 9) Error: type mismatch: got but expected one of: proc `[]`(s: string; i: BackwardsIndex): char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: string + but expression 'buf' is of type: cstring proc `[]`(s: var string; i: BackwardsIndex): var char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: var string + but expression 'buf' is of type: cstring proc `[]`[I: Ordinal; T](a: T; i: I): T first type mismatch at position: 0 proc `[]`[Idx, T; U, V: Ordinal](a: array[Idx, T]; x: HSlice[U, V]): seq[T] - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: array[Idx, T] + but expression 'buf' is of type: cstring proc `[]`[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: array[Idx, T] + but expression 'buf' is of type: cstring proc `[]`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: var array[Idx, T] + but expression 'buf' is of type: cstring proc `[]`[T, U: Ordinal](s: string; x: HSlice[T, U]): string - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: string + but expression 'buf' is of type: cstring proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T] - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: openArray[T] + but expression 'buf' is of type: cstring proc `[]`[T](s: openArray[T]; i: BackwardsIndex): T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: openArray[T] + but expression 'buf' is of type: cstring proc `[]`[T](s: var openArray[T]; i: BackwardsIndex): var T - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: var openArray[T] + but expression 'buf' is of type: cstring template `[]`(a: WideCStringObj; idx: int): Utf16Char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for a: WideCStringObj + but expression 'buf' is of type: cstring template `[]`(s: string; i: int): char - first type mismatch at position: 0 + first type mismatch at position: 1 + required type for s: string + but expression 'buf' is of type: cstring -expression: `[]`(buf, pos) -t10735.nim(44, 9) Error: expression '' has no type (or is ambiguous) -t10735.nim(46, 3) Error: illformed AST: case buf[pos] +expression: buf[pos] +t10735.nim(66, 9) Error: expression '' has no type (or is ambiguous) +t10735.nim(68, 3) Error: illformed AST: case buf[pos] ''' joinable: false """ diff --git a/tests/errmsgs/t22753.nim b/tests/errmsgs/t22753.nim index af6a871f13a66..8a504109a81ce 100644 --- a/tests/errmsgs/t22753.nim +++ b/tests/errmsgs/t22753.nim @@ -3,33 +3,50 @@ cmd: "nim check --hints:off $file" errormsg: "type mismatch" nimoutFull: true nimout: ''' -t22753.nim(34, 13) Error: array expects two type parameters -t22753.nim(35, 1) Error: expression 'x' has no type (or is ambiguous) -t22753.nim(35, 1) Error: expression 'x' has no type (or is ambiguous) -t22753.nim(35, 2) Error: type mismatch: got <> +t22753.nim(51, 13) Error: array expects two type parameters +t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous) +t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous) +t22753.nim(52, 2) Error: type mismatch: got <> but expected one of: proc `[]=`(s: var string; i: BackwardsIndex; x: char) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for i: BackwardsIndex + but expression '0' is of type: int literal(0) proc `[]=`[I: Ordinal; T, S](a: T; i: I; x: sink S) first type mismatch at position: 0 proc `[]=`[Idx, T; U, V: Ordinal](a: var array[Idx, T]; x: HSlice[U, V]; b: openArray[T]) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for x: HSlice[[]=.U, []=.V] + but expression '0' is of type: int literal(0) proc `[]=`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for i: BackwardsIndex + but expression '0' is of type: int literal(0) proc `[]=`[T, U: Ordinal](s: var string; x: HSlice[T, U]; b: string) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for x: HSlice[[]=.T, []=.U] + but expression '0' is of type: int literal(0) proc `[]=`[T; U, V: Ordinal](s: var seq[T]; x: HSlice[U, V]; b: openArray[T]) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for x: HSlice[[]=.U, []=.V] + but expression '0' is of type: int literal(0) proc `[]=`[T](s: var openArray[T]; i: BackwardsIndex; x: T) - first type mismatch at position: 0 + first type mismatch at position: 2 + required type for i: BackwardsIndex + but expression '0' is of type: int literal(0) template `[]=`(a: WideCStringObj; idx: int; val: Utf16Char) - first type mismatch at position: 0 + first type mismatch at position: 3 + required type for val: Utf16Char + but expression '9' is of type: int literal(9) template `[]=`(s: string; i: int; val: char) - first type mismatch at position: 0 + first type mismatch at position: 3 + required type for val: char + but expression '9' is of type: int literal(9) -expression: `[]=`(x, 0, 9) +expression: x[0] = 9 ''' """ + var x: array[3] # bug #22753 -x[0] = 9 \ No newline at end of file +x[0] = 9 diff --git a/tests/errmsgs/tsubscriptmismatch.nim b/tests/errmsgs/tsubscriptmismatch.nim new file mode 100644 index 0000000000000..a2b297b684b3a --- /dev/null +++ b/tests/errmsgs/tsubscriptmismatch.nim @@ -0,0 +1,11 @@ +discard """ + matrix: "-d:testsConciseTypeMismatch" + nimout: ''' +[1] proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T] +''' +""" + +type Foo = object +let x = Foo() +discard x[1] #[tt.Error + ^ type mismatch]# diff --git a/tests/errmsgs/tsubscriptmismatch_legacy.nim b/tests/errmsgs/tsubscriptmismatch_legacy.nim new file mode 100644 index 0000000000000..3e1f1eb71527c --- /dev/null +++ b/tests/errmsgs/tsubscriptmismatch_legacy.nim @@ -0,0 +1,10 @@ +discard """ + nimout: ''' + but expression 'x' is of type: Foo +''' +""" + +type Foo = object +let x = Foo() +discard x[1] #[tt.Error + ^ type mismatch: got ]#