handle explicit generic routine instantiations in sigmatch (#24010)

fixes #16376

The way the compiler handled generic proc instantiations in calls (like
`foo[int](...)`) up to this point was to instantiate `foo[int]`, create
a symbol for the instantiated proc (or a symchoice for multiple procs
excluding ones with mismatching generic param counts), then perform
overload resolution on this symbol/symchoice. The exception to this was
when the called symbol was already a symchoice node, in which case it
wasn't instantiated and overloading was called directly ([these
lines](b7b1313d21/compiler/semexprs.nim (L3366-L3371))).

This has several problems:

* Templates and macros can't create instantiated symbols, so they
couldn't participate in overloaded explicit generic instantiations,
causing the issue #16376.
* Every single proc that can be instantiated with the given generic
params is fully instantiated including the body. #9997 is about this but
isn't fixed here since the instantiation isn't in a call.

The way overload resolution handles explicit instantiations by itself is
also buggy:

* It doesn't check constraints.
* It allows only partially providing the generic parameters, which makes
sense for implicit generics, but can cause ambiguity in overloading.

Here is how this PR deals with these problems:

* Overload resolution now always handles explicit generic instantiations
in calls, in `initCandidate`, as long as the symbol resolves to a
routine symbol.
* Overload resolution now checks the generic params for constraints and
correct parameter count (ignoring implicit params). If these don't
match, the entire overload is considered as not matching and not
instantiated.
* Special error messages are added for mismatching/missing/extra generic
params. This is almost all of the diff in `semcall`.
* Procs with matching generic parameters now instantiate only the type
of the signature in overload resolution, not the proc itself, which also
works for templates and macros.

Unfortunately we can't entirely remove instantiations because overload
resolution can't handle some cases with uninstantiated types even though
it's resolved in the binding (see the last 2 blocks in
`texplicitgenerics`). There are also some instantiation issues with
default params that #24005 didn't fix but I didn't want this to become
the 3rd huge generics PR in a row so I didn't dive too deep into trying
to fix them. There is still a minor instantiation fix in `semtypinst`
though for subscripts in calls.

Additional changes:

* Overloading of `[]` wasn't documented properly, it somewhat is now
because we need to mention the limitation that it can't be done for
generic procs/types.
* Tests can now enable the new type mismatch errors with just
`-d:testsConciseTypeMismatch` in the command.

Package PRs:

- using fork for now:
[combparser](https://github.com/PMunch/combparser/pull/7) (partial
generic instantiation)
- merged: [cligen](https://github.com/c-blake/cligen/pull/233) (partial
generic instantiation but non-overloaded + template)
- merged: [neo](https://github.com/andreaferretti/neo/pull/56) (trying
to instantiate template with no generic param)
This commit is contained in:
metagn
2024-09-02 19:22:20 +03:00
committed by GitHub
parent c8af0996fd
commit 71de7fca9e
22 changed files with 436 additions and 118 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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..<n.len:
let formal = s.ast[genericParamsPos][i-1].typ
var arg = n[i].typ
# try transforming the argument into a static one before feeding it into
# typeRel
if formal.kind == tyStatic and arg.kind != tyStatic:
let evaluated = c.semTryConstExpr(c, n[i], n[i].typ)
if evaluated != nil:
arg = newTypeS(tyStatic, c, son = evaluated.typ)
arg.n = evaluated
let tm = typeRel(m, formal, arg)
if tm in {isNone, isConvertible}: return nil
matchGenericParams(m, n, s)
if m.state != csMatch:
# state is csMatch only if *all* generic params were matched,
# including implicit parameters
return nil
var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved
let info = getCallLineInfo(n)

View File

@@ -139,6 +139,9 @@ type
semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
info: TLineInfo): PSym
instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping,
prc: PSym, info: TLineInfo): PType
# used by sigmatch for explicit generic instantiations
includedFiles*: IntSet # used to detect recursive include files
pureEnumFields*: TStrTable # pure enum fields that can be used unambiguously
userPragmas*: TStrTable

View File

@@ -1070,14 +1070,6 @@ proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
result = initCandidate(c, t)
matches(c, n, nOrig, result)
proc bracketedMacro(n: PNode): PSym =
if n.len >= 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..<b.len:
if b[i].kind == nkSym and b[i].sym.magic == mArrGet:
let be = newNodeI(nkBracketExpr, n.info)
let name = b.getPIdent
if name != nil and name.s == "[]":
checkCallee(a[1])
if result:
# transform callee into normal bracket expr, only on success
let be = newNodeI(nkBracketExpr, a.info)
for i in 1..<a.len: be.add(a[i])
n[0] = be
return true
else:
result = false
proc asBracketExpr(c: PContext; n: PNode): PNode =
proc isGeneric(c: PContext; n: PNode): bool =
@@ -3365,11 +3373,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
else:
#liMessage(n.info, warnUser, renderTree(n));
result = semIndirectOp(c, n, flags, expectedType)
elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
isSymChoice(n[0][0]):
# indirectOp can deal with explicit instantiations; the fixes
# the 'newSeq[T](x)' bug
setGenericParams(c, n[0], nil)
elif isExplicitGenericCall(c, n): # this modifies `n` if true
result = semDirectOp(c, n, flags, expectedType)
elif nfDotField in n.flags:
result = semDirectOp(c, n, flags, expectedType)

View File

@@ -321,6 +321,22 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
prc.typ = result
popInfoContext(c.config)
proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType =
# instantiates only the type of a given proc symbol
# used by sigmatch for explicit generics
# 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 fakeSym = copySym(prc, c.idgen)
incl(fakeSym.flags, sfFromGeneric)
fakeSym.instantiatedFrom = prc
openScope(c)
for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
addDecl(c, s)
instantiateProcType(c, pt, fakeSym, info)
closeScope(c)
result = fakeSym.typ
proc fillMixinScope(c: PContext) =
var p = c.p
while p != nil:

View File

@@ -147,10 +147,14 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
# exception exists for the call name being a dot expression since
# dot expressions need their LHS instantiated
assert n.len != 0
let ignoreFirst = n[0].kind != nkDotExpr
# avoid instantiating generic proc symbols, refine condition if needed:
let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds
let name = n[0].getPIdent
let ignoreSecond = name != nil and name.s == "[]" and n.len > 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:

View File

@@ -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

View File

@@ -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

View File

@@ -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
"""

View File

@@ -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

View File

@@ -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)
w(v)

View File

@@ -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")

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -0,0 +1,25 @@
discard """
action: reject
nimout: '''
twrong_explicit_typeargs_legacy.nim(25, 29) Error: type mismatch: got <int literal(320), int literal(200)>
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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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]#

View File

@@ -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 <int literal(1), int literal(2)>
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 <int literal(1), int literal(2)>
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)]#

View File

@@ -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]())