Skip to content

Commit

Permalink
fixes #7222; fixes #5595; fixes #3747
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
zah committed Jun 16, 2018
1 parent cb62758 commit 06365ac
Show file tree
Hide file tree
Showing 15 changed files with 321 additions and 65 deletions.
1 change: 1 addition & 0 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions compiler/renderer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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!

Expand Down
2 changes: 1 addition & 1 deletion compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 19 additions & 4 deletions compiler/semcall.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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:
#
Expand All @@ -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]
Expand Down
30 changes: 30 additions & 0 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
31 changes: 20 additions & 11 deletions compiler/semgnrc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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 =
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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()
Expand All @@ -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)

37 changes: 20 additions & 17 deletions compiler/seminst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
35 changes: 23 additions & 12 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand All @@ -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}:
Expand All @@ -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))
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 06365ac

Please sign in to comment.