diff --git a/compiler/ast.nim b/compiler/ast.nim index eac4bf387fb59..32587e7a295dd 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1511,6 +1511,7 @@ proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son] +proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len) proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(typeof(a.k)): a.k = b.k diff --git a/compiler/sem.nim b/compiler/sem.nim index 58183261fa5d6..72d8dc5b040a2 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -728,6 +728,7 @@ proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PCo result.semOverloadedCall = semOverloadedCall result.semInferredLambda = semInferredLambda result.semGenerateInstance = generateInstance + result.instantiateOnlyProcType = instantiateOnlyProcType result.semTypeNode = semTypeNode result.instTypeBoundOp = sigmatch.instTypeBoundOp result.hasUnresolvedArgs = hasUnresolvedArgs diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 14e8591bae847..5fb61d5375265 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -249,6 +249,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" if n.len > 1: + const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam} if verboseTypeMismatch notin c.config.legacyFeatures: case err.firstMismatch.kind of kUnknownNamedParam: @@ -269,6 +270,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kMissingParam: candidates.add(" missing parameter: " & nameParam) candidates.add "\n" + of kExtraGenericParam: + candidates.add(" extra generic param given") + candidates.add "\n" + of kMissingGenericParam: + candidates.add(" missing generic parameter: " & nameParam) + candidates.add "\n" of kVarNeeded: doAssert nArg != nil doAssert err.firstMismatch.formal != nil @@ -292,9 +299,36 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) effectProblem(wanted, got, candidates, c) candidates.add "\n" + of kGenericParamTypeMismatch: + let pos = err.firstMismatch.arg + doAssert n[0].kind == nkBracketExpr and pos < n[0].len + let arg = n[0][pos] + doAssert arg != nil + var wanted = err.firstMismatch.formal.typ + if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint + let got = arg.typ + doAssert err.firstMismatch.formal != nil + doAssert wanted != nil + doAssert got != nil + candidates.add " generic parameter mismatch, expected " + candidates.addTypeDeclVerboseMaybe(c.config, wanted) + candidates.add " but got '" + candidates.add renderTree(arg) + candidates.add "' of type: " + candidates.addTypeDeclVerboseMaybe(c.config, got) + if got != nil and got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) + if got != nil: + effectProblem(wanted, got, candidates, c) + candidates.add "\n" of kUnknown: discard "do not break 'nim check'" else: candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) + if err.firstMismatch.kind in genericParamMismatches: + candidates.add(" in generic parameters") # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging case err.firstMismatch.kind of kUnknownNamedParam: @@ -306,20 +340,35 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kPositionalAlreadyGiven: candidates.add("\n positional param was already given as named param") of kExtraArg: candidates.add("\n extra argument given") of kMissingParam: candidates.add("\n missing parameter: " & nameParam) - of kTypeMismatch, kVarNeeded: - doAssert nArg != nil - let wanted = err.firstMismatch.formal.typ + of kExtraGenericParam: + candidates.add("\n extra generic param given") + of kMissingGenericParam: + candidates.add("\n missing generic parameter: " & nameParam) + of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded: + var arg: PNode = nArg + let genericMismatch = err.firstMismatch.kind == kGenericParamTypeMismatch + if genericMismatch: + let pos = err.firstMismatch.arg + doAssert n[0].kind == nkBracketExpr and pos < n[0].len + arg = n[0][pos] + else: + arg = nArg + doAssert arg != nil + var wanted = err.firstMismatch.formal.typ + if genericMismatch and wanted.kind == tyGenericParam and + wanted.genericParamHasConstraints: + wanted = wanted.genericConstraint doAssert err.firstMismatch.formal != nil candidates.add("\n required type for " & nameParam & ": ") candidates.addTypeDeclVerboseMaybe(c.config, wanted) candidates.add "\n but expression '" if err.firstMismatch.kind == kVarNeeded: - candidates.add renderNotLValue(nArg) + candidates.add renderNotLValue(arg) candidates.add "' is immutable, not 'var'" else: - candidates.add renderTree(nArg) + candidates.add renderTree(arg) candidates.add "' is of type: " - let got = nArg.typ + let got = arg.typ candidates.addTypeDeclVerboseMaybe(c.config, got) doAssert wanted != nil if got != nil: @@ -331,8 +380,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kUnknown: discard "do not break 'nim check'" candidates.add "\n" - if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and - n.kind == nkCommand: + if err.firstMismatch.arg == 1 and nArg != nil and + nArg.kind == nkTupleConstr and n.kind == nkCommand: maybeWrongSpace = true for diag in err.diagnostics: candidates.add(diag & "\n") @@ -780,21 +829,16 @@ proc explicitGenericInstError(c: PContext; n: PNode): PNode = result = n proc explicitGenericSym(c: PContext, n: PNode, s: PSym): 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") # binding has to stay 'nil' for this to work! var m = newCandidate(c, s, nil) - - for i in 1..= 1 and n[0].kind == nkSym: - result = n[0].sym - if result.kind notin {skMacro, skTemplate}: - result = nil - else: - result = nil - proc finishOperand(c: PContext, a: PNode): PNode = if a.typ.isNil: result = c.semOperand(c, a, {efDetermineType}) @@ -1167,11 +1159,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType let t = n[0].typ if t != nil and t.kind in {tyVar, tyLent}: n[0] = newDeref(n[0]) - elif n[0].kind == nkBracketExpr: - let s = bracketedMacro(n[0]) - if s != nil: - setGenericParams(c, n[0], s.ast[genericParamsPos]) - return semDirectOp(c, n, flags, expectedType) 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) @@ -1721,8 +1708,6 @@ proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode = result = explicitGenericInstantiation(c, n, s) if result == n: n[0] = copyTree(result[0]) - else: - n[0] = result proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if not a built-in subscript operator; also called for the @@ -3013,19 +2998,42 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp else: result = tupexp -proc shouldBeBracketExpr(n: PNode): bool = - result = false +proc isExplicitGenericCall(c: PContext, n: PNode): bool = + ## checks if a call node `n` is a routine call with explicit generic params + ## + ## the callee node needs to be either an nkBracketExpr or a call to a + ## symchoice of `[]` in which case it will be transformed into nkBracketExpr + ## + ## the LHS of the bracket expr has to either be a symchoice or resolve to + ## a routine symbol + template checkCallee(n: PNode) = + # check subscript LHS, `n` must be mutable + if isSymChoice(n): + result = true + else: + let s = qualifiedLookUp(c, n, {}) + if s != nil and s.kind in routineKinds: + result = true + n = semSymGenericInstantiation(c, n, s) assert n.kind in nkCallKinds + result = false let a = n[0] - if a.kind in nkCallKinds: + case a.kind + of nkBracketExpr: + checkCallee(a[0]) + of nkCallKinds: let b = a[0] if b.kind in nkSymChoices: - for i in 0.. 1 and - (n[1].typ != nil and n[1].typ.kind == tyTypeDesc) + # generic type instantiation: + ((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or + # generic proc instantiation: + (n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict)) if ignoreFirst: result.add(n[0]) else: @@ -168,7 +172,10 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = # dot expressions need their LHS instantiated assert n.len != 0 let ignoreFirst = n[0].kind != nkDotExpr and - n[0].typ != nil and n[0].typ.kind == tyTypeDesc + # generic type instantiation: + ((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or + # generic proc instantiation: + (n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict)) if ignoreFirst: result.add(n[0]) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 89b66b52473db..b7efa8e4511f9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -23,7 +23,8 @@ when defined(nimPreviewSlimSystem): type MismatchKind* = enum kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded, - kMissingParam, kExtraArg, kPositionalAlreadyGiven + kMissingParam, kExtraArg, kPositionalAlreadyGiven, + kGenericParamTypeMismatch, kMissingGenericParam, kExtraGenericParam MismatchInfo* = object kind*: MismatchKind # reason for mismatch @@ -129,6 +130,103 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = echo "binding ", key, " -> ", val idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen)) +proc typeRel*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation + +proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) = + var arg = n.typ + # fix up the type to get ready to match formal: + var formalBase = formal + while formalBase.kind == tyGenericParam and + formalBase.genericParamHasConstraints: + formalBase = formalBase.genericConstraint + if formalBase.kind == tyStatic and arg.kind != tyStatic: + # maybe call `paramTypesMatch` here, for now be conservative + if n.kind in nkSymChoices: n.flags.excl nfSem + let evaluated = m.c.semTryConstExpr(m.c, n, formalBase.skipTypes({tyStatic})) + if evaluated != nil: + arg = newTypeS(tyStatic, m.c, son = evaluated.typ) + arg.n = evaluated + elif formalBase.kind == tyTypeDesc: + if arg.kind != tyTypeDesc: + arg = makeTypeDesc(m.c, arg) + else: + arg = arg.skipTypes({tyTypeDesc}) + let tm = typeRel(m, formal, arg) + if tm in {isNone, isConvertible}: + m.state = csNoMatch + m.firstMismatch.kind = kGenericParamTypeMismatch + return + +proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) = + ## matches explicit generic instantiation `binding` against generic params of + ## proc symbol `callee` + ## state is set to `csMatch` if all generic params match, `csEmpty` if + ## implicit generic parameters are missing (matches but cannot instantiate), + ## `csNoMatch` if a constraint fails or param count doesn't match + let c = m.c + let typeParams = callee.ast[genericParamsPos] + let paramCount = typeParams.len + let bindingCount = binding.len-1 + if bindingCount > paramCount: + m.state = csNoMatch + m.firstMismatch.kind = kExtraGenericParam + m.firstMismatch.arg = paramCount + 1 + return + for i in 1..bindingCount: + matchGenericParam(m, typeParams[i-1].typ, binding[i]) + if m.state == csNoMatch: + m.firstMismatch.arg = i + m.firstMismatch.formal = typeParams[i-1].sym + return + # not enough generic params given, check if remaining have defaults: + for i in bindingCount ..< paramCount: + let param = typeParams[i] + assert param.kind == nkSym + let paramSym = param.sym + if paramSym.ast != nil: + matchGenericParam(m, param.typ, paramSym.ast) + if m.state == csNoMatch: + m.firstMismatch.arg = i + 1 + m.firstMismatch.formal = paramSym + return + elif tfImplicitTypeParam in paramSym.typ.flags: + # not a mismatch, but can't create sym + m.state = csEmpty + return + else: + m.state = csNoMatch + m.firstMismatch.kind = kMissingGenericParam + m.firstMismatch.arg = i + 1 + m.firstMismatch.formal = paramSym + return + m.state = csMatch + +proc copyingEraseVoidParams(m: TCandidate, t: var PType) = + ## if `t` is a proc type with void parameters, copies it and erases them + assert t.kind == tyProc + let original = t + var copied = false + for i in 1 ..< original.len: + var f = original[i] + var isVoidParam = f.kind == tyVoid + if not isVoidParam: + let prev = idTableGet(m.bindings, f) + if prev != nil: f = prev + isVoidParam = f.kind == tyVoid + if isVoidParam: + if not copied: + # keep first i children + t = copyType(original, m.c.idgen, t.owner) + t.setSonsLen(i) + t.n = copyNode(original.n) + t.n.sons = original.n.sons + t.n.sons.setLen(i) + copied = true + elif copied: + t.add(f) + t.n.add(original.n[i]) + proc initCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1, diagnosticsEnabled = false): TCandidate = @@ -143,17 +241,20 @@ proc initCandidate*(ctx: PContext, callee: PSym, result.magic = result.calleeSym.magic result.bindings = initTypeMapping() if binding != nil and callee.kind in routineKinds: - var typeParams = callee.ast[genericParamsPos] - for i in 1..min(typeParams.len, binding.len-1): - var formalTypeParam = typeParams[i-1].typ - var bound = binding[i].typ - if bound != nil: - if formalTypeParam.kind == tyTypeDesc: - if bound.kind != tyTypeDesc: - bound = makeTypeDesc(ctx, bound) - else: - bound = bound.skipTypes({tyTypeDesc}) - put(result, formalTypeParam, bound) + matchGenericParams(result, binding, callee) + let genericMatch = result.state + if genericMatch != csNoMatch: + result.state = csEmpty + if genericMatch == csMatch: # csEmpty if not fully instantiated + # instantiate the type, emulates old compiler behavior + # wouldn't be needed if sigmatch could handle complex cases, + # examples are in texplicitgenerics + # might be buggy, see rest of generateInstance if problems occur + let typ = ctx.instantiateOnlyProcType(ctx, result.bindings, callee, binding.info) + result.callee = typ + else: + # createThread[void] requires this if the above branch is removed: + copyingEraseVoidParams(result, result.callee) proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = @@ -176,9 +277,6 @@ proc copyCandidate(dest: var TCandidate, src: TCandidate) = dest.baseTypeMatch = src.baseTypeMatch dest.bindings = src.bindings -proc typeRel*(c: var TCandidate, f, aOrig: PType, - flags: TTypeRelFlags = {}): TTypeRelation - proc checkGeneric(a, b: TCandidate): int = let c = a.c let aa = a.callee @@ -702,6 +800,8 @@ proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation = proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = case a.kind of tyProc: + var f = f + copyingEraseVoidParams(c, f) if f.signatureLen != a.signatureLen: return result = isEqual # start with maximum; also correct for no # params at all @@ -2792,6 +2892,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = inc m.genericMatches inc m.exactMatches return + # initCandidate may have given csNoMatch if generic params didn't match: + if m.state == csNoMatch: return var marker = initIntSet() matchesAux(c, n, nOrig, m, marker) if m.state == csNoMatch: return diff --git a/doc/manual.md b/doc/manual.md index 04574945009ae..693fb7330124e 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -4454,7 +4454,42 @@ as an example: Overloading of the subscript operator ------------------------------------- -The `[]` subscript operator for arrays/openarrays/sequences can be overloaded. +The `[]` subscript operator for arrays/openarrays/sequences can be overloaded +for any type (with some exceptions) by defining a routine with the name `[]`. + + ```nim + type Foo = object + data: seq[int] + + proc `[]`(foo: Foo, i: int): int = + result = foo.data[i] + + let foo = Foo(data: @[1, 2, 3]) + echo foo[1] # 2 + ``` + +Assignment to subscripts can also be overloaded by naming a routine `[]=`, +which has precedence over assigning to the result of `[]`. + + ```nim + type Foo = object + data: seq[int] + + proc `[]`(foo: Foo, i: int): int = + result = foo.data[i] + proc `[]=`(foo: var Foo, i: int, val: int) = + foo.data[i] = val + + var foo = Foo(data: @[1, 2, 3]) + echo foo[1] # 2 + foo[1] = 5 + echo foo.data # @[1, 5, 3] + echo foo[1] # 5 + ``` + +Overloads of the subscript operator cannot be applied to routine or type +symbols themselves, as this conflicts with the syntax for instantiating +generic parameters, i.e. `foo[int](1, 2, 3)` or `Foo[int]`. Methods diff --git a/nimsuggest/tests/tgeneric_highlight.nim b/nimsuggest/tests/tgeneric_highlight.nim index f351ab705f3f3..c7291d08b8cc1 100644 --- a/nimsuggest/tests/tgeneric_highlight.nim +++ b/nimsuggest/tests/tgeneric_highlight.nim @@ -7,12 +7,7 @@ $nimsuggest --tester $file >highlight $1 highlight;;skType;;1;;7;;3 highlight;;skProc;;1;;0;;6 -highlight;;skProc;;1;;0;;6 -highlight;;skProc;;1;;0;;6 highlight;;skType;;2;;14;;3 highlight;;skProc;;2;;7;;6 -highlight;;skProc;;2;;7;;6 -highlight;;skProc;;2;;7;;6 -highlight;;skTemplate;;3;;0;;8 highlight;;skType;;3;;9;;3 """ diff --git a/testament/important_packages.nim b/testament/important_packages.nim index f8849f00dfc6c..8ac28df10f48f 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -99,7 +99,6 @@ pkg "macroutils" pkg "manu" pkg "markdown" pkg "measuremancer", "nimble testDeps; nimble -y test" -# when unchained is version 0.3.7 or higher, use `nimble testDeps;` pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" @@ -144,7 +143,8 @@ pkg "pnm" pkg "polypbren" pkg "presto" pkg "prologue", "nimble tcompile" -pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim" +# remove fork after https://github.com/PMunch/combparser/pull/7 is merged: +pkg "protobuf", "nimble install -y https://github.com/metagn/combparser@#HEAD; nim c -o:protobuff -r src/protobuf.nim" pkg "rbtree" pkg "react", "nimble example" pkg "regex", "nim c src/regex" @@ -181,7 +181,7 @@ pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" pkg "union", "nim c -r tests/treadme.nim", url = "https://github.com/alaviss/union" pkg "unittest2" pkg "unpack" -pkg "weave", "nimble test_gc_arc", useHead = true +pkg "weave", "nimble install cligen@#HEAD; nimble test_gc_arc", useHead = true pkg "websock" pkg "websocket", "nim c websocket.nim" # pkg "winim", allowFailure = true diff --git a/tests/ccgbugs/t20141.nim b/tests/ccgbugs/t20141.nim index 499cd21aa032b..60e1306905ff8 100644 --- a/tests/ccgbugs/t20141.nim +++ b/tests/ccgbugs/t20141.nim @@ -16,7 +16,7 @@ template n[T, U](x: U): T = proc k() = var res: A - m(n[B](res)) + m(n[B, A](res)) proc w(mounter: U) = discard @@ -24,4 +24,4 @@ proc mount(proto: U) = discard proc v() = mount k # This is required for failure -w(v) \ No newline at end of file +w(v) diff --git a/tests/config.nims b/tests/config.nims index 31610e128c0db..0b2b66d81b6a0 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -43,6 +43,7 @@ switch("define", "nimPreviewRangeDefault") switch("define", "nimPreviewNonVarDestructor") switch("warningAserror", "UnnamedBreak") -switch("legacy", "verboseTypeMismatch") +when not defined(testsConciseTypeMismatch): + switch("legacy", "verboseTypeMismatch") switch("experimental", "vtables") switch("experimental", "openSym") diff --git a/tests/errmsgs/tconcisetypemismatch.nim b/tests/errmsgs/tconcisetypemismatch.nim index c2896604f5557..3093cc24e55b7 100644 --- a/tests/errmsgs/tconcisetypemismatch.nim +++ b/tests/errmsgs/tconcisetypemismatch.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --hints:off --skipParentCfg $file" + cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file" errormsg: "type mismatch" nimout: ''' tconcisetypemismatch.nim(23, 47) Error: type mismatch diff --git a/tests/errmsgs/tconcisetypemismatch.nims b/tests/errmsgs/tconcisetypemismatch.nims deleted file mode 100644 index e9dce814747ea..0000000000000 --- a/tests/errmsgs/tconcisetypemismatch.nims +++ /dev/null @@ -1,21 +0,0 @@ -switch("path", "$lib/../testament/lib") - # so we can `import stdtest/foo` inside tests - # Using $lib/../ instead of $nim/ so you can use a different nim to run tests - # during local testing, e.g. nim --lib:lib. - -## prevent common user config settings to interfere with testament expectations -## Indifidual tests can override this if needed to test for these options. -switch("colors", "off") - -switch("excessiveStackTrace", "off") - -when (NimMajor, NimMinor, NimPatch) >= (1,5,1): - # to make it easier to test against older nim versions, (best effort only) - switch("filenames", "legacyRelProj") - switch("spellSuggest", "0") - -# for std/unittest -switch("define", "nimUnittestOutputLevel:PRINT_FAILURES") -switch("define", "nimUnittestColor:off") - -hint("Processing", off) diff --git a/tests/errmsgs/twrong_explicit_typeargs.nim b/tests/errmsgs/twrong_explicit_typeargs.nim new file mode 100644 index 0000000000000..705eec52bfe6d --- /dev/null +++ b/tests/errmsgs/twrong_explicit_typeargs.nim @@ -0,0 +1,26 @@ +discard """ + cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file" + action: reject + nimout: ''' +twrong_explicit_typeargs.nim(26, 29) Error: type mismatch +Expression: newImage[string](320, 200) + [1] 320: int literal(320) + [2] 200: int literal(200) + +Expected one of (first mismatch at [position]): +[1] proc newImage[T: int32 | int64](w, h: int): ref Image[T] + generic parameter mismatch, expected int32 or int64 but got 'string' of type: typedesc[string] +''' +""" + +# bug #4084 +type + Image[T] = object + data: seq[T] + +proc newImage[T: int32|int64](w, h: int): ref Image[T] = + new(result) + result.data = newSeq[T](w * h) + +var correct = newImage[int32](320, 200) +var wrong = newImage[string](320, 200) diff --git a/tests/errmsgs/twrong_explicit_typeargs_legacy.nim b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim new file mode 100644 index 0000000000000..fb81412dcf322 --- /dev/null +++ b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim @@ -0,0 +1,25 @@ +discard """ + action: reject + nimout: ''' +twrong_explicit_typeargs_legacy.nim(25, 29) Error: type mismatch: got +but expected one of: +proc newImage[T: int32 | int64](w, h: int): ref Image[T] + first type mismatch at position: 1 in generic parameters + required type for T: int32 or int64 + but expression 'string' is of type: typedesc[string] + +expression: newImage[string](320, 200) +''' +""" + +# bug #4084 +type + Image[T] = object + data: seq[T] + +proc newImage[T: int32|int64](w, h: int): ref Image[T] = + new(result) + result.data = newSeq[T](w * h) + +var correct = newImage[int32](320, 200) +var wrong = newImage[string](320, 200) diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim index ad0d1e88f3f8a..7220b7429d53a 100644 --- a/tests/generics/timplicit_and_explicit.nim +++ b/tests/generics/timplicit_and_explicit.nim @@ -3,8 +3,8 @@ block: # basic test proc doStuff[T](a: SomeInteger): T = discard proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard assert typeof(doStuff[int](100)) is int - assert typeof(doStuff[int](100, 1.0)) is float - assert typeof(doStuff[int](100, "Hello")) is string + assert typeof(doStuff[int, float](100, 1.0)) is float + assert typeof(doStuff[int, string](100, "Hello")) is string proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z) diff --git a/tests/generics/twrong_explicit_typeargs.nim b/tests/generics/twrong_explicit_typeargs.nim deleted file mode 100644 index e47b38e99ed59..0000000000000 --- a/tests/generics/twrong_explicit_typeargs.nim +++ /dev/null @@ -1,16 +0,0 @@ -discard """ - errormsg: "cannot instantiate: 'newImage[string]'" - line: 16 -""" - -# bug #4084 -type - Image[T] = object - data: seq[T] - -proc newImage[T: int32|int64](w, h: int): ref Image[T] = - new(result) - result.data = newSeq[T](w * h) - -var correct = newImage[int32](320, 200) -var wrong = newImage[string](320, 200) diff --git a/tests/proc/texplicitgenericcount.nim b/tests/proc/texplicitgenericcount.nim new file mode 100644 index 0000000000000..8654a1d136d57 --- /dev/null +++ b/tests/proc/texplicitgenericcount.nim @@ -0,0 +1,24 @@ +discard """ + cmd: "nim check -d:testsConciseTypeMismatch $file" +""" + +proc foo[T, U](x: T, y: U): (T, U) = (x, y) + +let x = foo[int](1, 2) #[tt.Error + ^ type mismatch +Expression: foo[int](1, 2) + [1] 1: int literal(1) + [2] 2: int literal(2) + +Expected one of (first mismatch at [position]): +[2] proc foo[T, U](x: T; y: U): (T, U) + missing generic parameter: U]# +let y = foo[int, float, string](1, 2) #[tt.Error + ^ type mismatch +Expression: foo[int, float, string](1, 2) + [1] 1: int literal(1) + [2] 2: int literal(2) + +Expected one of (first mismatch at [position]): +[3] proc foo[T, U](x: T; y: U): (T, U) + extra generic param given]# diff --git a/tests/proc/texplicitgenericcountverbose.nim b/tests/proc/texplicitgenericcountverbose.nim new file mode 100644 index 0000000000000..76228eeafc3b6 --- /dev/null +++ b/tests/proc/texplicitgenericcountverbose.nim @@ -0,0 +1,22 @@ +discard """ + cmd: "nim check $file" +""" + +proc foo[T, U](x: T, y: U): (T, U) = (x, y) + +let x = foo[int](1, 2) #[tt.Error + ^ type mismatch: got +but expected one of: +proc foo[T, U](x: T; y: U): (T, U) + first type mismatch at position: 2 in generic parameters + missing generic parameter: U + +expression: foo[int](1, 2)]# +let y = foo[int, float, string](1, 2) #[tt.Error + ^ type mismatch: got +but expected one of: +proc foo[T, U](x: T; y: U): (T, U) + first type mismatch at position: 3 in generic parameters + extra generic param given + +expression: foo[int, float, string](1, 2)]# diff --git a/tests/proc/texplicitgenerics.nim b/tests/proc/texplicitgenerics.nim new file mode 100644 index 0000000000000..c0bdfe513d794 --- /dev/null +++ b/tests/proc/texplicitgenerics.nim @@ -0,0 +1,49 @@ +block: # issue #16376 + type + Matrix[T] = object + data: T + proc randMatrix[T](m, n: int, max: T): Matrix[T] = discard + proc randMatrix[T](m, n: int, x: Slice[T]): Matrix[T] = discard + template randMatrix[T](m, n: int): Matrix[T] = randMatrix[T](m, n, T(1.0)) + let B = randMatrix[float32](20, 10) + +block: # different generic param counts + type + Matrix[T] = object + data: T + proc randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0)) + proc randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U)) + let b = randMatrix[float32](20, 10) + doAssert b == Matrix[float32](data: 1.0) + +block: # above for templates + type + Matrix[T] = object + data: T + template randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0)) + template randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U)) + let b = randMatrix[float32](20, 10) + doAssert b == Matrix[float32](data: 1.0) + +block: # sigmatch can't handle this without pre-instantiating the type: + # minimized from numericalnim + type Foo[T] = proc (x: T) + proc foo[T](x: T) = discard + proc bar[T](f: Foo[T]) = discard + bar[int](foo) + +block: # ditto but may be wrong minimization + # minimized from measuremancer + type Foo[T] = object + proc foo[T](): Foo[T] = Foo[T]() + when false: + # this is the actual issue but there are other instantiation problems + proc bar[T](x = foo[T]()) = discard + else: + proc bar[T](x: Foo[T] = foo[T]()) = discard + bar[int](Foo[int]()) + bar[int]() + when false: + # alternative version, also causes instantiation issue + proc baz[T](x: typeof(foo[T]())) = discard + baz[int](Foo[int]())