mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-13 06:43:52 +00:00
* late instantiation for the generic procs' default param values * automatic mixin behaviour in concepts Other fixes: * don't render the automatically inserted default params in calls * better rendering of tyFromExpr
This commit is contained in:
@@ -454,6 +454,7 @@ type
|
||||
nfPreventCg # this node should be ignored by the codegen
|
||||
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
||||
nfFromTemplate # a top-level node returned from a template
|
||||
nfDefaultParam # an automatically inserter default parameter
|
||||
|
||||
TNodeFlags* = set[TNodeFlag]
|
||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
|
||||
|
||||
@@ -387,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
|
||||
assert(theEnd < 0)
|
||||
result = 0
|
||||
for i in countup(start, sonsLen(n) + theEnd):
|
||||
inc(result, lsub(g, n.sons[i]))
|
||||
inc(result, 2) # for ``, ``
|
||||
let param = n.sons[i]
|
||||
if nfDefaultParam notin param.flags:
|
||||
inc(result, lsub(g, param))
|
||||
inc(result, 2) # for ``, ``
|
||||
if result > 0:
|
||||
dec(result, 2) # last does not get a comma!
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
|
||||
proc semStaticExpr(c: PContext, n: PNode): PNode
|
||||
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
|
||||
proc semTypeOf(c: PContext; n: PNode): PNode
|
||||
|
||||
proc hasUnresolvedArgs(c: PContext, n: PNode): bool
|
||||
proc isArrayConstr(n: PNode): bool {.inline.} =
|
||||
result = n.kind == nkBracket and
|
||||
n.typ.skipTypes(abstractInst).kind == tyArray
|
||||
|
||||
@@ -391,7 +391,21 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
||||
result = copyTree(arg)
|
||||
result.typ = formal
|
||||
|
||||
proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||
proc updateDefaultParams(call: PNode) =
|
||||
# In generic procs, the default parameter may be unique for each
|
||||
# instantiation (see tlateboundgenericparams).
|
||||
# After a call is resolved, we need to re-assign any default value
|
||||
# that was used during sigmatch. sigmatch is responsible for marking
|
||||
# the default params with `nfDefaultParam` and `instantiateProcType`
|
||||
# computes correctly the default values for each instantiation.
|
||||
let calleeParams = call[0].sym.typ.n
|
||||
for i in countdown(call.len - 1, 1):
|
||||
if nfDefaultParam notin call[i].flags:
|
||||
return
|
||||
call[i] = calleeParams[i].sym.ast
|
||||
|
||||
proc semResolvedCall(c: PContext, x: TCandidate,
|
||||
n: PNode, flags: TExprFlags): PNode =
|
||||
assert x.state == csMatch
|
||||
var finalCallee = x.calleeSym
|
||||
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
|
||||
@@ -424,8 +438,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
|
||||
|
||||
result = x.call
|
||||
instGenericConvertersSons(c, result, x)
|
||||
result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
|
||||
result[0] = newSymNode(finalCallee, result[0].info)
|
||||
result.typ = finalCallee.typ.sons[0]
|
||||
updateDefaultParams(result)
|
||||
|
||||
proc canDeref(n: PNode): bool {.inline.} =
|
||||
result = n.len >= 2 and (let t = n[1].typ;
|
||||
@@ -447,7 +462,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
message(c.config, n.info, hintUserRaw,
|
||||
"Non-matching candidates for " & renderTree(n) & "\n" &
|
||||
candidates)
|
||||
result = semResolvedCall(c, n, r)
|
||||
result = semResolvedCall(c, r, n, flags)
|
||||
elif implicitDeref in c.features and canDeref(n):
|
||||
# try to deref the first argument and then try overloading resolution again:
|
||||
#
|
||||
@@ -458,7 +473,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
|
||||
#
|
||||
n.sons[1] = n.sons[1].tryDeref
|
||||
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
|
||||
if r.state == csMatch: result = semResolvedCall(c, n, r)
|
||||
if r.state == csMatch: result = semResolvedCall(c, r, n, flags)
|
||||
else:
|
||||
# get rid of the deref again for a better error message:
|
||||
n.sons[1] = n.sons[1].sons[0]
|
||||
|
||||
@@ -542,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) =
|
||||
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
|
||||
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
|
||||
|
||||
proc isUnresolvedSym(s: PSym): bool =
|
||||
return s.kind == skGenericParam or
|
||||
tfInferrableStatic in s.typ.flags or
|
||||
(s.kind == skParam and s.typ.isMetaType) or
|
||||
(s.kind == skType and
|
||||
s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
|
||||
|
||||
proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
|
||||
# Checks whether an expression depends on generic parameters that
|
||||
# don't have bound values yet. E.g. this could happen in situations
|
||||
# such as:
|
||||
# type Slot[T] = array[T.size, byte]
|
||||
# proc foo[T](x: default(T))
|
||||
#
|
||||
# Both static parameter and type parameters can be unresolved.
|
||||
case n.kind
|
||||
of nkSym:
|
||||
return isUnresolvedSym(n.sym)
|
||||
of nkIdent, nkAccQuoted:
|
||||
let ident = considerQuotedIdent(c.config, n)
|
||||
let sym = searchInScopes(c, ident)
|
||||
if sym != nil:
|
||||
return isUnresolvedSym(sym)
|
||||
else:
|
||||
return false
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if hasUnresolvedArgs(c, n.sons[i]): return true
|
||||
return false
|
||||
|
||||
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
|
||||
sfCompileToCpp in c.module.flags):
|
||||
|
||||
@@ -32,9 +32,12 @@ type
|
||||
cursorInBody: bool # only for nimsuggest
|
||||
bracketExpr: PNode
|
||||
|
||||
type
|
||||
TSemGenericFlag = enum
|
||||
withinBind, withinTypeDesc, withinMixin, withinConcept
|
||||
withinBind,
|
||||
withinTypeDesc,
|
||||
withinMixin,
|
||||
withinConcept
|
||||
|
||||
TSemGenericFlags = set[TSemGenericFlag]
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode,
|
||||
@@ -53,8 +56,15 @@ template macroToExpand(s): untyped =
|
||||
template macroToExpandSym(s): untyped =
|
||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
|
||||
|
||||
template isMixedIn(sym): bool =
|
||||
let s = sym
|
||||
s.name.id in ctx.toMixin or (withinConcept in flags and
|
||||
s.magic == mNone and
|
||||
s.kind in OverloadableSyms)
|
||||
|
||||
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
|
||||
ctx: var GenericCtx; fromDotExpr=false): PNode =
|
||||
ctx: var GenericCtx; flags: TSemGenericFlags,
|
||||
fromDotExpr=false): PNode =
|
||||
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||
incl(s.flags, sfUsed)
|
||||
case s.kind
|
||||
@@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
else:
|
||||
if withinBind in flags:
|
||||
result = symChoice(c, n, s, scClosed)
|
||||
elif s.name.id in ctx.toMixin:
|
||||
elif s.isMixedIn:
|
||||
result = symChoice(c, n, s, scForceOpen)
|
||||
else:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
# else: leave as nkIdent
|
||||
|
||||
proc newDot(n, b: PNode): PNode =
|
||||
@@ -135,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
|
||||
var s = qualifiedLookUp(c, n, luf)
|
||||
if s != nil:
|
||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
||||
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||
else:
|
||||
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
|
||||
result = n
|
||||
@@ -146,10 +156,10 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||
isMacro = s.kind in {skTemplate, skMacro}
|
||||
if withinBind in flags:
|
||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
||||
elif s.name.id in ctx.toMixin:
|
||||
elif s.isMixedIn:
|
||||
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||
else:
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
|
||||
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||
if syms.kind == nkSym:
|
||||
let choice = symChoice(c, n, s, scForceOpen)
|
||||
choice.kind = nkClosedSymChoice
|
||||
@@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
if s != nil:
|
||||
incl(s.flags, sfUsed)
|
||||
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
||||
let sc = symChoice(c, fn, s,
|
||||
if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
|
||||
let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen)
|
||||
case s.kind
|
||||
of skMacro:
|
||||
if macroToExpand(s) and sc.safeLen <= 1:
|
||||
@@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||
when defined(nimsuggest):
|
||||
if withinTypeDesc in flags: dec c.inTypeContext
|
||||
|
||||
|
||||
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
||||
var ctx: GenericCtx
|
||||
ctx.toMixin = initIntset()
|
||||
@@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
|
||||
ctx.toMixin = initIntset()
|
||||
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||
|
||||
|
||||
@@ -247,25 +247,28 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||
if i > 1:
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||
propagateToOwner(result, result.sons[i])
|
||||
let needsStaticSkipping = result[i].kind == tyFromExpr
|
||||
result[i] = replaceTypeVarsT(cl, result[i])
|
||||
if needsStaticSkipping:
|
||||
result[i] = result[i].skipTypes({tyStatic})
|
||||
internalAssert c.config, originalParams[i].kind == nkSym
|
||||
when true:
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
param.typ = result.sons[i]
|
||||
if oldParam.ast != nil:
|
||||
param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info)
|
||||
let oldParam = originalParams[i].sym
|
||||
let param = copySym(oldParam)
|
||||
param.owner = prc
|
||||
param.typ = result[i]
|
||||
if oldParam.ast != nil:
|
||||
var def = oldParam.ast.copyTree
|
||||
if def.kind == nkCall:
|
||||
for i in 1 ..< def.len:
|
||||
def[i] = replaceTypeVarsN(cl, def[i])
|
||||
def = semExprWithType(c, def)
|
||||
param.ast = fitNode(c, param.typ, def, def.info)
|
||||
param.typ = param.ast.typ
|
||||
|
||||
# don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])!
|
||||
result.n.sons[i] = newSymNode(param)
|
||||
addDecl(c, param)
|
||||
else:
|
||||
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||
result.n.sons[i] = param
|
||||
param.sym.owner = prc
|
||||
addDecl(c, result.n.sons[i].sym)
|
||||
result.n[i] = newSymNode(param)
|
||||
result[i] = param.typ
|
||||
propagateToOwner(result, result[i])
|
||||
addDecl(c, param)
|
||||
|
||||
resetIdTable(cl.symMap)
|
||||
resetIdTable(cl.localCache)
|
||||
|
||||
@@ -244,7 +244,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
||||
|
||||
for i in 0..1:
|
||||
if hasGenericArguments(range[i]):
|
||||
if hasUnresolvedArgs(c, range[i]):
|
||||
result.n.addSon makeStaticExpr(c, range[i])
|
||||
result.flags.incl tfUnresolved
|
||||
else:
|
||||
@@ -301,8 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||
localError(c.config, info, errOrdinalTypeExpected)
|
||||
result = makeRangeWithStaticExpr(c, e)
|
||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||
elif e.kind in (nkCallKinds + {nkBracketExpr}) and
|
||||
hasGenericArguments(e):
|
||||
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
|
||||
if not isOrdinalType(e.typ):
|
||||
localError(c.config, n[1].info, errOrdinalTypeExpected)
|
||||
# This is an int returning call, depending on an
|
||||
@@ -1006,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
prev: PType, kind: TSymKind; isType=false): PType =
|
||||
# for historical reasons (code grows) this is invoked for parameter
|
||||
# lists too and then 'isType' is false.
|
||||
var cl: IntSet
|
||||
checkMinSonsLen(n, 1, c.config)
|
||||
result = newProcType(c, n.info, prev)
|
||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
||||
cl = initIntSet()
|
||||
var check = initIntSet()
|
||||
var counter = 0
|
||||
|
||||
for i in countup(1, n.len - 1):
|
||||
var a = n.sons[i]
|
||||
if a.kind != nkIdentDefs:
|
||||
@@ -1022,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
# pass over this instantiation:
|
||||
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
|
||||
illFormedAst(a, c.config)
|
||||
|
||||
checkMinSonsLen(a, 3, c.config)
|
||||
var
|
||||
typ: PType = nil
|
||||
@@ -1030,26 +1028,38 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
length = sonsLen(a)
|
||||
hasType = a.sons[length-2].kind != nkEmpty
|
||||
hasDefault = a.sons[length-1].kind != nkEmpty
|
||||
|
||||
if hasType:
|
||||
typ = semParamType(c, a.sons[length-2], constraint)
|
||||
|
||||
if hasDefault:
|
||||
def = semExprWithType(c, a.sons[length-1])
|
||||
# check type compatibility between def.typ and typ:
|
||||
def = a[^1]
|
||||
block determineType:
|
||||
if genericParams != nil and genericParams.len > 0:
|
||||
def = semGenericStmt(c, def)
|
||||
if hasUnresolvedArgs(c, def):
|
||||
def.typ = makeTypeFromExpr(c, def.copyTree)
|
||||
break determineType
|
||||
|
||||
def = semExprWithType(c, def, {efDetermineType})
|
||||
|
||||
if typ == nil:
|
||||
typ = def.typ
|
||||
elif def != nil:
|
||||
# and def.typ != nil and def.typ.kind != tyNone:
|
||||
else:
|
||||
# if def.typ != nil and def.typ.kind != tyNone:
|
||||
# example code that triggers it:
|
||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||
if not containsGenericType(typ):
|
||||
# check type compatibility between def.typ and typ:
|
||||
def = fitNode(c, typ, def, def.info)
|
||||
|
||||
if not hasType and not hasDefault:
|
||||
if isType: localError(c.config, a.info, "':' expected")
|
||||
if kind in {skTemplate, skMacro}:
|
||||
typ = newTypeS(tyExpr, c)
|
||||
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
||||
continue
|
||||
|
||||
for j in countup(0, length-3):
|
||||
var arg = newSymG(skParam, a.sons[j], c)
|
||||
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
||||
@@ -1065,7 +1075,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||
arg.position = counter
|
||||
arg.constraint = constraint
|
||||
inc(counter)
|
||||
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
|
||||
if def != nil and def.kind != nkEmpty:
|
||||
arg.ast = copyTree(def)
|
||||
if containsOrIncl(check, arg.name.id):
|
||||
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||
addSon(result.n, newSymNode(arg))
|
||||
@@ -1320,7 +1331,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
incl dummyParam.flags, sfUsed
|
||||
addDecl(c, dummyParam)
|
||||
|
||||
result.n.sons[3] = semConceptBody(c, n[3])
|
||||
result.n[3] = semConceptBody(c, n[3])
|
||||
closeScope(c)
|
||||
|
||||
proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||
|
||||
@@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool =
|
||||
(n.sym.kind == skGenericParam or
|
||||
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
|
||||
|
||||
proc hasGenericArguments*(n: PNode): bool =
|
||||
if n.kind == nkSym:
|
||||
return n.sym.kind == skGenericParam or
|
||||
tfInferrableStatic in n.sym.typ.flags or
|
||||
(n.sym.kind == skType and
|
||||
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
|
||||
else:
|
||||
for i in 0..<n.safeLen:
|
||||
if hasGenericArguments(n.sons[i]): return true
|
||||
return false
|
||||
|
||||
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||
# This is needed for tgenericshardcases
|
||||
# It's possible that a generic param will be used in a proc call to a
|
||||
@@ -231,6 +220,16 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
|
||||
# symbol is not our business:
|
||||
if cl.owner != nil and s.owner != cl.owner:
|
||||
return s
|
||||
|
||||
# XXX: Bound symbols in default parameter expressions may reach here.
|
||||
# We cannot process them, becase `sym.n` may point to a proc body with
|
||||
# cyclic references that will lead to an infinite recursion. Perhaps we
|
||||
# should not use a black-list here, but a whitelist instead.
|
||||
# Note: `s.magic` may be `mType` in an example such as:
|
||||
# proc foo[T](a: T, b = myDefault(type(a)))
|
||||
if s.kind == skProc or s.magic != mNone:
|
||||
return s
|
||||
|
||||
#result = PSym(idTableGet(cl.symMap, s))
|
||||
#if result == nil:
|
||||
result = copySym(s, false)
|
||||
@@ -278,7 +277,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
# is difficult to handle:
|
||||
const eqFlags = eqTypeFlags + {tfGcSafe}
|
||||
var body = t.sons[0]
|
||||
if body.kind != tyGenericBody: internalError(cl.c.config, cl.info, "no generic body")
|
||||
if body.kind != tyGenericBody:
|
||||
internalError(cl.c.config, cl.info, "no generic body")
|
||||
var header: PType = t
|
||||
# search for some instantiation here:
|
||||
if cl.allowMetaTypes:
|
||||
|
||||
@@ -1089,8 +1089,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
else: isNone
|
||||
|
||||
of tyAnything:
|
||||
return if f.kind == tyAnything: isGeneric
|
||||
else: isNone
|
||||
if f.kind in {tyAnything}:
|
||||
return isGeneric
|
||||
|
||||
if tfWildCard in a.flags and f.kind == tyTypeDesc:
|
||||
return isGeneric
|
||||
|
||||
return isNone
|
||||
|
||||
of tyUserTypeClass, tyUserTypeClassInst:
|
||||
if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
|
||||
@@ -1711,9 +1716,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||
# proc foo(T: typedesc, x: T)
|
||||
# when `f` is an unresolved typedesc, `a` could be any
|
||||
# type, so we should not perform this check earlier
|
||||
if a.kind != tyTypeDesc: return isNone
|
||||
|
||||
if f.base.kind == tyNone:
|
||||
if a.kind != tyTypeDesc:
|
||||
if a.kind == tyGenericParam and tfWildcard in a.flags:
|
||||
# TODO: prevent `a` from matching as a wildcard again
|
||||
result = isGeneric
|
||||
else:
|
||||
result = isNone
|
||||
elif f.base.kind == tyNone:
|
||||
result = isGeneric
|
||||
else:
|
||||
result = typeRel(c, f.base, a.base)
|
||||
@@ -2363,7 +2372,11 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
|
||||
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
|
||||
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
||||
put(m, formal.typ, def.typ)
|
||||
def.flags.incl nfDefaultParam
|
||||
setSon(m.call, formal.position + 1, def)
|
||||
# XXX: Instead of setting a default value here, we may place a special
|
||||
# marker value instead. Later, we will replace it in `semResolvedCall`.
|
||||
# Unfortunately, this causes some breakage at the moment.
|
||||
inc(f)
|
||||
# forget all inferred types if the overload matching failed
|
||||
if m.state == csNoMatch:
|
||||
|
||||
@@ -502,7 +502,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
#internalAssert t.len == 0
|
||||
result = "untyped"
|
||||
of tyFromExpr:
|
||||
result = renderTree(t.n)
|
||||
if t.n == nil:
|
||||
result = "unknown"
|
||||
else:
|
||||
result = "type(" & renderTree(t.n) & ")"
|
||||
of tyArray:
|
||||
if t.sons[0].kind == tyRange:
|
||||
result = "array[" & rangeToStr(t.sons[0].n) & ", " &
|
||||
|
||||
26
tests/concepts/libs/trie.nim
Normal file
26
tests/concepts/libs/trie.nim
Normal file
@@ -0,0 +1,26 @@
|
||||
import
|
||||
hashes, tables, trie_database
|
||||
|
||||
type
|
||||
MemDBTable = Table[KeccakHash, string]
|
||||
|
||||
MemDB* = object
|
||||
tbl: MemDBTable
|
||||
|
||||
proc hash*(key: KeccakHash): int =
|
||||
hashes.hash(key.data)
|
||||
|
||||
proc get*(db: MemDB, key: KeccakHash): string =
|
||||
db.tbl[key]
|
||||
|
||||
proc del*(db: var MemDB, key: KeccakHash): bool =
|
||||
if db.tbl.hasKey(key):
|
||||
db.tbl.del(key)
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
proc put*(db: var MemDB, key: KeccakHash, value: string): bool =
|
||||
db.tbl[key] = value
|
||||
return true
|
||||
|
||||
12
tests/concepts/libs/trie_database.nim
Normal file
12
tests/concepts/libs/trie_database.nim
Normal file
@@ -0,0 +1,12 @@
|
||||
type
|
||||
KeccakHash* = object
|
||||
data*: string
|
||||
|
||||
BytesRange* = object
|
||||
bytes*: string
|
||||
|
||||
TrieDatabase* = concept db
|
||||
put(var db, KeccakHash, string) is bool
|
||||
del(var db, KeccakHash) is bool
|
||||
get(db, KeccakHash) is string
|
||||
|
||||
7
tests/concepts/ttrieconcept.nim
Normal file
7
tests/concepts/ttrieconcept.nim
Normal file
@@ -0,0 +1,7 @@
|
||||
import libs/[trie_database, trie]
|
||||
|
||||
proc takeDb(d: TrieDatabase) = discard
|
||||
var mdb: MemDB
|
||||
|
||||
takeDb(mdb)
|
||||
|
||||
124
tests/generics/tlateboundgenericparams.nim
Normal file
124
tests/generics/tlateboundgenericparams.nim
Normal file
@@ -0,0 +1,124 @@
|
||||
import typetraits
|
||||
|
||||
type
|
||||
Foo = object
|
||||
|
||||
proc defaultFoo: Foo = discard
|
||||
proc defaultInt: int = 1
|
||||
proc defaultTInt(T: type): int = 2
|
||||
proc defaultTFoo[T](x: typedesc[T]): Foo = discard
|
||||
proc defaultTOldSchool[T](x: typedesc[T]): T = discard
|
||||
proc defaultTModern(T: type): T = discard
|
||||
|
||||
proc specializedDefault(T: type int): int = 10
|
||||
proc specializedDefault(T: type string): string = "default"
|
||||
|
||||
converter intFromFoo(x: Foo): int = 3
|
||||
|
||||
proc consumeInt(x: int) =
|
||||
discard
|
||||
|
||||
const activeTests = {1..100}
|
||||
|
||||
when true:
|
||||
template test(n, body) =
|
||||
when n in activeTests:
|
||||
block:
|
||||
body
|
||||
|
||||
template reject(x) =
|
||||
static: assert(not compiles(x))
|
||||
|
||||
test 1:
|
||||
proc t[T](val: T = defaultInt()) =
|
||||
consumeInt val
|
||||
|
||||
t[int]()
|
||||
reject t[string]()
|
||||
|
||||
test 2:
|
||||
proc t1[T](val: T = defaultFoo()) =
|
||||
static:
|
||||
assert type(val).name == "int"
|
||||
assert T.name == "int"
|
||||
|
||||
consumeInt val
|
||||
|
||||
# here, the converter should kick in, but notice
|
||||
# how `val` is still typed `int` inside the proc.
|
||||
t1[int]()
|
||||
|
||||
proc t2[T](val: T = defaultFoo()) =
|
||||
discard
|
||||
|
||||
reject t2[string]()
|
||||
|
||||
test 3:
|
||||
proc tInt[T](val = defaultInt()): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tInt[int]() == "int"
|
||||
doAssert tInt[string]() == "int"
|
||||
|
||||
proc tInt2[T](val = defaultTInt(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tInt2[int]() == "int"
|
||||
doAssert tInt2[string]() == "int"
|
||||
|
||||
proc tDefTModern[T](val = defaultTModern(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tDefTModern[int]() == "int"
|
||||
doAssert tDefTModern[string]() == "string"
|
||||
doAssert tDefTModern[Foo]() == "Foo"
|
||||
|
||||
proc tDefTOld[T](val = defaultTOldSchool(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert tDefTOld[int]() == "int"
|
||||
doAssert tDefTOld[string]() == "string"
|
||||
doAssert tDefTOld[Foo]() == "Foo"
|
||||
|
||||
test 4:
|
||||
proc t[T](val: T = defaultTFoo(T)): string =
|
||||
return type(val).name
|
||||
|
||||
doAssert t[int]() == "int"
|
||||
doAssert t[Foo]() == "Foo"
|
||||
reject t[string]()
|
||||
|
||||
test 5:
|
||||
proc t1[T](a: T = specializedDefault(T)): T =
|
||||
return a
|
||||
|
||||
doAssert t1[int]() == 10
|
||||
doAssert t1[string]() == "default"
|
||||
|
||||
proc t2[T](a: T, b = specializedDefault(T)): auto =
|
||||
return $a & $b
|
||||
|
||||
doAssert t2(5) == "510"
|
||||
doAssert t2("string ") == "string default"
|
||||
|
||||
proc t3[T](a: T, b = specializedDefault(type(a))): auto =
|
||||
return $a & $b
|
||||
|
||||
doAssert t3(100) == "10010"
|
||||
doAssert t3("another ") == "another default"
|
||||
|
||||
test 6:
|
||||
# https://github.com/nim-lang/Nim/issues/5595
|
||||
type
|
||||
Point[T] = object
|
||||
x, y: T
|
||||
|
||||
proc getOrigin[T](): Point[T] = Point[T](x: 0, y: 0)
|
||||
|
||||
proc rotate[T](point: Point[T], radians: float,
|
||||
origin = getOrigin[T]()): Point[T] =
|
||||
discard
|
||||
|
||||
var p = getOrigin[float]()
|
||||
var rotated = p.rotate(2.1)
|
||||
|
||||
Reference in New Issue
Block a user