* 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:
parent
ea36e0ebbe
commit
5bcf8bcb59
15 changed files with 321 additions and 65 deletions
|
|
@ -454,6 +454,7 @@ type
|
||||||
nfPreventCg # this node should be ignored by the codegen
|
nfPreventCg # this node should be ignored by the codegen
|
||||||
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
|
||||||
nfFromTemplate # a top-level node returned from a template
|
nfFromTemplate # a top-level node returned from a template
|
||||||
|
nfDefaultParam # an automatically inserter default parameter
|
||||||
|
|
||||||
TNodeFlags* = set[TNodeFlag]
|
TNodeFlags* = set[TNodeFlag]
|
||||||
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
|
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)
|
assert(theEnd < 0)
|
||||||
result = 0
|
result = 0
|
||||||
for i in countup(start, sonsLen(n) + theEnd):
|
for i in countup(start, sonsLen(n) + theEnd):
|
||||||
inc(result, lsub(g, n.sons[i]))
|
let param = n.sons[i]
|
||||||
inc(result, 2) # for ``, ``
|
if nfDefaultParam notin param.flags:
|
||||||
|
inc(result, lsub(g, param))
|
||||||
|
inc(result, 2) # for ``, ``
|
||||||
if result > 0:
|
if result > 0:
|
||||||
dec(result, 2) # last does not get a comma!
|
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 semStaticExpr(c: PContext, n: PNode): PNode
|
||||||
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
|
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
|
||||||
proc semTypeOf(c: PContext; n: PNode): PNode
|
proc semTypeOf(c: PContext; n: PNode): PNode
|
||||||
|
proc hasUnresolvedArgs(c: PContext, n: PNode): bool
|
||||||
proc isArrayConstr(n: PNode): bool {.inline.} =
|
proc isArrayConstr(n: PNode): bool {.inline.} =
|
||||||
result = n.kind == nkBracket and
|
result = n.kind == nkBracket and
|
||||||
n.typ.skipTypes(abstractInst).kind == tyArray
|
n.typ.skipTypes(abstractInst).kind == tyArray
|
||||||
|
|
|
||||||
|
|
@ -391,7 +391,21 @@ proc inferWithMetatype(c: PContext, formal: PType,
|
||||||
result = copyTree(arg)
|
result = copyTree(arg)
|
||||||
result.typ = formal
|
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
|
assert x.state == csMatch
|
||||||
var finalCallee = x.calleeSym
|
var finalCallee = x.calleeSym
|
||||||
markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym)
|
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
|
result = x.call
|
||||||
instGenericConvertersSons(c, result, x)
|
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]
|
result.typ = finalCallee.typ.sons[0]
|
||||||
|
updateDefaultParams(result)
|
||||||
|
|
||||||
proc canDeref(n: PNode): bool {.inline.} =
|
proc canDeref(n: PNode): bool {.inline.} =
|
||||||
result = n.len >= 2 and (let t = n[1].typ;
|
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,
|
message(c.config, n.info, hintUserRaw,
|
||||||
"Non-matching candidates for " & renderTree(n) & "\n" &
|
"Non-matching candidates for " & renderTree(n) & "\n" &
|
||||||
candidates)
|
candidates)
|
||||||
result = semResolvedCall(c, n, r)
|
result = semResolvedCall(c, r, n, flags)
|
||||||
elif implicitDeref in c.features and canDeref(n):
|
elif implicitDeref in c.features and canDeref(n):
|
||||||
# try to deref the first argument and then try overloading resolution again:
|
# 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
|
n.sons[1] = n.sons[1].tryDeref
|
||||||
var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
|
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:
|
else:
|
||||||
# get rid of the deref again for a better error message:
|
# get rid of the deref again for a better error message:
|
||||||
n.sons[1] = n.sons[1].sons[0]
|
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 =
|
proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
|
||||||
result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
|
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 =
|
proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
|
||||||
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
|
if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or
|
||||||
sfCompileToCpp in c.module.flags):
|
sfCompileToCpp in c.module.flags):
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,12 @@ type
|
||||||
cursorInBody: bool # only for nimsuggest
|
cursorInBody: bool # only for nimsuggest
|
||||||
bracketExpr: PNode
|
bracketExpr: PNode
|
||||||
|
|
||||||
type
|
|
||||||
TSemGenericFlag = enum
|
TSemGenericFlag = enum
|
||||||
withinBind, withinTypeDesc, withinMixin, withinConcept
|
withinBind,
|
||||||
|
withinTypeDesc,
|
||||||
|
withinMixin,
|
||||||
|
withinConcept
|
||||||
|
|
||||||
TSemGenericFlags = set[TSemGenericFlag]
|
TSemGenericFlags = set[TSemGenericFlag]
|
||||||
|
|
||||||
proc semGenericStmt(c: PContext, n: PNode,
|
proc semGenericStmt(c: PContext, n: PNode,
|
||||||
|
|
@ -53,8 +56,15 @@ template macroToExpand(s): untyped =
|
||||||
template macroToExpandSym(s): untyped =
|
template macroToExpandSym(s): untyped =
|
||||||
s.kind in {skMacro, skTemplate} and (s.typ.len == 1) and not fromDotExpr
|
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,
|
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)
|
semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
case s.kind
|
case s.kind
|
||||||
|
|
@ -115,10 +125,10 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
else:
|
else:
|
||||||
if withinBind in flags:
|
if withinBind in flags:
|
||||||
result = symChoice(c, n, s, scClosed)
|
result = symChoice(c, n, s, scClosed)
|
||||||
elif s.name.id in ctx.toMixin:
|
elif s.isMixedIn:
|
||||||
result = symChoice(c, n, s, scForceOpen)
|
result = symChoice(c, n, s, scForceOpen)
|
||||||
else:
|
else:
|
||||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||||
# else: leave as nkIdent
|
# else: leave as nkIdent
|
||||||
|
|
||||||
proc newDot(n, b: PNode): PNode =
|
proc newDot(n, b: PNode): PNode =
|
||||||
|
|
@ -135,7 +145,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
|
|
||||||
var s = qualifiedLookUp(c, n, luf)
|
var s = qualifiedLookUp(c, n, luf)
|
||||||
if s != nil:
|
if s != nil:
|
||||||
result = semGenericStmtSymbol(c, n, s, ctx)
|
result = semGenericStmtSymbol(c, n, s, ctx, flags)
|
||||||
else:
|
else:
|
||||||
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
|
n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx)
|
||||||
result = n
|
result = n
|
||||||
|
|
@ -146,10 +156,10 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
|
||||||
isMacro = s.kind in {skTemplate, skMacro}
|
isMacro = s.kind in {skTemplate, skMacro}
|
||||||
if withinBind in flags:
|
if withinBind in flags:
|
||||||
result = newDot(result, symChoice(c, n, s, scClosed))
|
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))
|
result = newDot(result, symChoice(c, n, s, scForceOpen))
|
||||||
else:
|
else:
|
||||||
let syms = semGenericStmtSymbol(c, n, s, ctx, fromDotExpr=true)
|
let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
|
||||||
if syms.kind == nkSym:
|
if syms.kind == nkSym:
|
||||||
let choice = symChoice(c, n, s, scForceOpen)
|
let choice = symChoice(c, n, s, scForceOpen)
|
||||||
choice.kind = nkClosedSymChoice
|
choice.kind = nkClosedSymChoice
|
||||||
|
|
@ -215,8 +225,7 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
if s != nil:
|
if s != nil:
|
||||||
incl(s.flags, sfUsed)
|
incl(s.flags, sfUsed)
|
||||||
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles}
|
||||||
let sc = symChoice(c, fn, s,
|
let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen)
|
||||||
if s.name.id in ctx.toMixin: scForceOpen else: scOpen)
|
|
||||||
case s.kind
|
case s.kind
|
||||||
of skMacro:
|
of skMacro:
|
||||||
if macroToExpand(s) and sc.safeLen <= 1:
|
if macroToExpand(s) and sc.safeLen <= 1:
|
||||||
|
|
@ -472,7 +481,6 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
when defined(nimsuggest):
|
when defined(nimsuggest):
|
||||||
if withinTypeDesc in flags: dec c.inTypeContext
|
if withinTypeDesc in flags: dec c.inTypeContext
|
||||||
|
|
||||||
|
|
||||||
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
proc semGenericStmt(c: PContext, n: PNode): PNode =
|
||||||
var ctx: GenericCtx
|
var ctx: GenericCtx
|
||||||
ctx.toMixin = initIntset()
|
ctx.toMixin = initIntset()
|
||||||
|
|
@ -484,3 +492,4 @@ proc semConceptBody(c: PContext, n: PNode): PNode =
|
||||||
ctx.toMixin = initIntset()
|
ctx.toMixin = initIntset()
|
||||||
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
result = semGenericStmt(c, n, {withinConcept}, ctx)
|
||||||
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -247,25 +247,28 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
if i > 1:
|
if i > 1:
|
||||||
resetIdTable(cl.symMap)
|
resetIdTable(cl.symMap)
|
||||||
resetIdTable(cl.localCache)
|
resetIdTable(cl.localCache)
|
||||||
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
let needsStaticSkipping = result[i].kind == tyFromExpr
|
||||||
propagateToOwner(result, result.sons[i])
|
result[i] = replaceTypeVarsT(cl, result[i])
|
||||||
|
if needsStaticSkipping:
|
||||||
|
result[i] = result[i].skipTypes({tyStatic})
|
||||||
internalAssert c.config, originalParams[i].kind == nkSym
|
internalAssert c.config, originalParams[i].kind == nkSym
|
||||||
when true:
|
let oldParam = originalParams[i].sym
|
||||||
let oldParam = originalParams[i].sym
|
let param = copySym(oldParam)
|
||||||
let param = copySym(oldParam)
|
param.owner = prc
|
||||||
param.owner = prc
|
param.typ = result[i]
|
||||||
param.typ = result.sons[i]
|
if oldParam.ast != nil:
|
||||||
if oldParam.ast != nil:
|
var def = oldParam.ast.copyTree
|
||||||
param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info)
|
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[i] = newSymNode(param)
|
||||||
result.n.sons[i] = newSymNode(param)
|
result[i] = param.typ
|
||||||
addDecl(c, param)
|
propagateToOwner(result, result[i])
|
||||||
else:
|
addDecl(c, param)
|
||||||
let param = replaceTypeVarsN(cl, originalParams[i])
|
|
||||||
result.n.sons[i] = param
|
|
||||||
param.sym.owner = prc
|
|
||||||
addDecl(c, result.n.sons[i].sym)
|
|
||||||
|
|
||||||
resetIdTable(cl.symMap)
|
resetIdTable(cl.symMap)
|
||||||
resetIdTable(cl.localCache)
|
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]))
|
localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
|
||||||
|
|
||||||
for i in 0..1:
|
for i in 0..1:
|
||||||
if hasGenericArguments(range[i]):
|
if hasUnresolvedArgs(c, range[i]):
|
||||||
result.n.addSon makeStaticExpr(c, range[i])
|
result.n.addSon makeStaticExpr(c, range[i])
|
||||||
result.flags.incl tfUnresolved
|
result.flags.incl tfUnresolved
|
||||||
else:
|
else:
|
||||||
|
|
@ -301,8 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
|
||||||
localError(c.config, info, errOrdinalTypeExpected)
|
localError(c.config, info, errOrdinalTypeExpected)
|
||||||
result = makeRangeWithStaticExpr(c, e)
|
result = makeRangeWithStaticExpr(c, e)
|
||||||
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
if c.inGenericContext > 0: result.flags.incl tfUnresolved
|
||||||
elif e.kind in (nkCallKinds + {nkBracketExpr}) and
|
elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
|
||||||
hasGenericArguments(e):
|
|
||||||
if not isOrdinalType(e.typ):
|
if not isOrdinalType(e.typ):
|
||||||
localError(c.config, n[1].info, errOrdinalTypeExpected)
|
localError(c.config, n[1].info, errOrdinalTypeExpected)
|
||||||
# This is an int returning call, depending on an
|
# 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 =
|
prev: PType, kind: TSymKind; isType=false): PType =
|
||||||
# for historical reasons (code grows) this is invoked for parameter
|
# for historical reasons (code grows) this is invoked for parameter
|
||||||
# lists too and then 'isType' is false.
|
# lists too and then 'isType' is false.
|
||||||
var cl: IntSet
|
|
||||||
checkMinSonsLen(n, 1, c.config)
|
checkMinSonsLen(n, 1, c.config)
|
||||||
result = newProcType(c, n.info, prev)
|
result = newProcType(c, n.info, prev)
|
||||||
if genericParams != nil and sonsLen(genericParams) == 0:
|
|
||||||
cl = initIntSet()
|
|
||||||
var check = initIntSet()
|
var check = initIntSet()
|
||||||
var counter = 0
|
var counter = 0
|
||||||
|
|
||||||
for i in countup(1, n.len - 1):
|
for i in countup(1, n.len - 1):
|
||||||
var a = n.sons[i]
|
var a = n.sons[i]
|
||||||
if a.kind != nkIdentDefs:
|
if a.kind != nkIdentDefs:
|
||||||
|
|
@ -1022,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
# pass over this instantiation:
|
# pass over this instantiation:
|
||||||
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
|
if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue
|
||||||
illFormedAst(a, c.config)
|
illFormedAst(a, c.config)
|
||||||
|
|
||||||
checkMinSonsLen(a, 3, c.config)
|
checkMinSonsLen(a, 3, c.config)
|
||||||
var
|
var
|
||||||
typ: PType = nil
|
typ: PType = nil
|
||||||
|
|
@ -1030,26 +1028,38 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
|
||||||
length = sonsLen(a)
|
length = sonsLen(a)
|
||||||
hasType = a.sons[length-2].kind != nkEmpty
|
hasType = a.sons[length-2].kind != nkEmpty
|
||||||
hasDefault = a.sons[length-1].kind != nkEmpty
|
hasDefault = a.sons[length-1].kind != nkEmpty
|
||||||
|
|
||||||
if hasType:
|
if hasType:
|
||||||
typ = semParamType(c, a.sons[length-2], constraint)
|
typ = semParamType(c, a.sons[length-2], constraint)
|
||||||
|
|
||||||
if hasDefault:
|
if hasDefault:
|
||||||
def = semExprWithType(c, a.sons[length-1])
|
def = a[^1]
|
||||||
# check type compatibility between def.typ and typ:
|
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:
|
if typ == nil:
|
||||||
typ = def.typ
|
typ = def.typ
|
||||||
elif def != nil:
|
else:
|
||||||
# and def.typ != nil and def.typ.kind != tyNone:
|
# if def.typ != nil and def.typ.kind != tyNone:
|
||||||
# example code that triggers it:
|
# example code that triggers it:
|
||||||
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
# proc sort[T](cmp: proc(a, b: T): int = cmp)
|
||||||
if not containsGenericType(typ):
|
if not containsGenericType(typ):
|
||||||
|
# check type compatibility between def.typ and typ:
|
||||||
def = fitNode(c, typ, def, def.info)
|
def = fitNode(c, typ, def, def.info)
|
||||||
|
|
||||||
if not hasType and not hasDefault:
|
if not hasType and not hasDefault:
|
||||||
if isType: localError(c.config, a.info, "':' expected")
|
if isType: localError(c.config, a.info, "':' expected")
|
||||||
if kind in {skTemplate, skMacro}:
|
if kind in {skTemplate, skMacro}:
|
||||||
typ = newTypeS(tyExpr, c)
|
typ = newTypeS(tyExpr, c)
|
||||||
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for j in countup(0, length-3):
|
for j in countup(0, length-3):
|
||||||
var arg = newSymG(skParam, a.sons[j], c)
|
var arg = newSymG(skParam, a.sons[j], c)
|
||||||
if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
|
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.position = counter
|
||||||
arg.constraint = constraint
|
arg.constraint = constraint
|
||||||
inc(counter)
|
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):
|
if containsOrIncl(check, arg.name.id):
|
||||||
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'")
|
||||||
addSon(result.n, newSymNode(arg))
|
addSon(result.n, newSymNode(arg))
|
||||||
|
|
@ -1320,7 +1331,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||||
incl dummyParam.flags, sfUsed
|
incl dummyParam.flags, sfUsed
|
||||||
addDecl(c, dummyParam)
|
addDecl(c, dummyParam)
|
||||||
|
|
||||||
result.n.sons[3] = semConceptBody(c, n[3])
|
result.n[3] = semConceptBody(c, n[3])
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
|
||||||
proc semProcTypeWithScope(c: PContext, n: PNode,
|
proc semProcTypeWithScope(c: PContext, n: PNode,
|
||||||
|
|
|
||||||
|
|
@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool =
|
||||||
(n.sym.kind == skGenericParam or
|
(n.sym.kind == skGenericParam or
|
||||||
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
|
(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 =
|
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
# This is needed for tgenericshardcases
|
# This is needed for tgenericshardcases
|
||||||
# It's possible that a generic param will be used in a proc call to a
|
# 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:
|
# symbol is not our business:
|
||||||
if cl.owner != nil and s.owner != cl.owner:
|
if cl.owner != nil and s.owner != cl.owner:
|
||||||
return s
|
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))
|
#result = PSym(idTableGet(cl.symMap, s))
|
||||||
#if result == nil:
|
#if result == nil:
|
||||||
result = copySym(s, false)
|
result = copySym(s, false)
|
||||||
|
|
@ -278,7 +277,8 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# is difficult to handle:
|
# is difficult to handle:
|
||||||
const eqFlags = eqTypeFlags + {tfGcSafe}
|
const eqFlags = eqTypeFlags + {tfGcSafe}
|
||||||
var body = t.sons[0]
|
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
|
var header: PType = t
|
||||||
# search for some instantiation here:
|
# search for some instantiation here:
|
||||||
if cl.allowMetaTypes:
|
if cl.allowMetaTypes:
|
||||||
|
|
|
||||||
|
|
@ -1089,8 +1089,13 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
|
||||||
else: isNone
|
else: isNone
|
||||||
|
|
||||||
of tyAnything:
|
of tyAnything:
|
||||||
return if f.kind == tyAnything: isGeneric
|
if f.kind in {tyAnything}:
|
||||||
else: isNone
|
return isGeneric
|
||||||
|
|
||||||
|
if tfWildCard in a.flags and f.kind == tyTypeDesc:
|
||||||
|
return isGeneric
|
||||||
|
|
||||||
|
return isNone
|
||||||
|
|
||||||
of tyUserTypeClass, tyUserTypeClassInst:
|
of tyUserTypeClass, tyUserTypeClassInst:
|
||||||
if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
|
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)
|
# proc foo(T: typedesc, x: T)
|
||||||
# when `f` is an unresolved typedesc, `a` could be any
|
# when `f` is an unresolved typedesc, `a` could be any
|
||||||
# type, so we should not perform this check earlier
|
# type, so we should not perform this check earlier
|
||||||
if a.kind != tyTypeDesc: return isNone
|
if a.kind != tyTypeDesc:
|
||||||
|
if a.kind == tyGenericParam and tfWildcard in a.flags:
|
||||||
if f.base.kind == tyNone:
|
# TODO: prevent `a` from matching as a wildcard again
|
||||||
|
result = isGeneric
|
||||||
|
else:
|
||||||
|
result = isNone
|
||||||
|
elif f.base.kind == tyNone:
|
||||||
result = isGeneric
|
result = isGeneric
|
||||||
else:
|
else:
|
||||||
result = typeRel(c, f.base, a.base)
|
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)
|
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
|
||||||
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
|
||||||
put(m, formal.typ, def.typ)
|
put(m, formal.typ, def.typ)
|
||||||
|
def.flags.incl nfDefaultParam
|
||||||
setSon(m.call, formal.position + 1, def)
|
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)
|
inc(f)
|
||||||
# forget all inferred types if the overload matching failed
|
# forget all inferred types if the overload matching failed
|
||||||
if m.state == csNoMatch:
|
if m.state == csNoMatch:
|
||||||
|
|
|
||||||
|
|
@ -502,7 +502,10 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
#internalAssert t.len == 0
|
#internalAssert t.len == 0
|
||||||
result = "untyped"
|
result = "untyped"
|
||||||
of tyFromExpr:
|
of tyFromExpr:
|
||||||
result = renderTree(t.n)
|
if t.n == nil:
|
||||||
|
result = "unknown"
|
||||||
|
else:
|
||||||
|
result = "type(" & renderTree(t.n) & ")"
|
||||||
of tyArray:
|
of tyArray:
|
||||||
if t.sons[0].kind == tyRange:
|
if t.sons[0].kind == tyRange:
|
||||||
result = "array[" & rangeToStr(t.sons[0].n) & ", " &
|
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)
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue