fixes #11225; generic sandwich problems; [backport:1.2] (#17255)

* fixes #11225; generic sandwich problems; [backport:1.2]
* progress
* delegating these symbols must be done via 'bind'
This commit is contained in:
Andreas Rumpf 2021-03-09 20:19:24 +01:00 • committed by GitHub
commit 2f213db7ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 132 additions and 21 deletions

View file

@ -2909,6 +2909,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
inc p.splitDecls inc p.splitDecls
genGotoState(p, n) genGotoState(p, n)
of nkBreakState: genBreakState(p, n, d) of nkBreakState: genBreakState(p, n, d)
of nkMixinStmt, nkBindStmt: discard
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope = proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope =

View file

@ -860,7 +860,8 @@ proc containsResult(n: PNode): bool =
for i in 0..<n.safeLen: for i in 0..<n.safeLen:
if containsResult(n[i]): return true if containsResult(n[i]): return true
const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef, nkMacroDef} + const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
nkMacroDef, nkMixinStmt, nkBindStmt} +
declarativeDefs declarativeDefs
proc easyResultAsgn(n: PNode): PNode = proc easyResultAsgn(n: PNode): PNode =

View file

@ -157,7 +157,7 @@ type
const const
nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt, nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
nkCommentStmt} + procDefs nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
proc newStateAccess(ctx: var Ctx): PNode = proc newStateAccess(ctx: var Ctx): PNode =
if ctx.stateVarSym.isNil: if ctx.stateVarSym.isNil:

View file

@ -884,7 +884,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
result = n result = n
of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock: of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock:

View file

@ -2626,7 +2626,8 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
of nkRaiseStmt: genRaiseStmt(p, n) of nkRaiseStmt: genRaiseStmt(p, n)
of nkTypeSection, nkCommentStmt, nkIncludeStmt, of nkTypeSection, nkCommentStmt, nkIncludeStmt,
nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt,
nkMixinStmt, nkBindStmt: discard
of nkIteratorDef: of nkIteratorDef:
if n[0].sym.typ.callConv == TCallingConvention.ccClosure: if n[0].sym.typ.callConv == TCallingConvention.ccClosure:
globalError(p.config, n.info, "Closure iterators are not supported by JS backend!") globalError(p.config, n.info, "Closure iterators are not supported by JS backend!")

View file

@ -497,7 +497,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
w = up w = up
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr: nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
discard discard
of nkLambdaKinds, nkIteratorDef: of nkLambdaKinds, nkIteratorDef:
if n.typ != nil: if n.typ != nil:
@ -752,7 +753,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
result = accessViaEnvVar(n, owner, d, c) result = accessViaEnvVar(n, owner, d, c)
of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom, of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef, nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef,
nkMacroDef, nkFuncDef: nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt:
discard discard
of nkClosure: of nkClosure:
if n[1].kind == nkNilLit: if n[1].kind == nkNilLit:

View file

@ -43,7 +43,7 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) =
of nkSym: of nkSym:
if interestingVar(it.sym): if interestingVar(it.sym):
n[i] = lookupOrAdd(c, it.sym, it.info) n[i] = lookupOrAdd(c, it.sym, it.info)
of procDefs, nkTypeSection: discard of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard
else: else:
for i in 0..<it.safeLen: for i in 0..<it.safeLen:
liftLocals(it, i, c) liftLocals(it, i, c)

View file

@ -1260,7 +1260,8 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
discard "don't follow this : same as varpartitions" discard "don't follow this : same as varpartitions"
result = Check(nilability: Nil, map: map) result = Check(nilability: Nil, map: map)

View file

@ -150,7 +150,8 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
nkTypeOfExpr, nkMixinStmt, nkBindStmt:
discard "do not follow the construct" discard "do not follow the construct"
of nkAsgn, nkFastAsgn: of nkAsgn, nkFastAsgn:
@ -249,7 +250,8 @@ proc opt(c: Con; n, parent: PNode; parentPos: int) =
of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr,
nkMixinStmt, nkBindStmt:
parent[parentPos] = n parent[parentPos] = n
else: else:

View file

@ -105,6 +105,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
decl(a[1]) decl(a[1])
else: else:
for i in 0..<n.safeLen: deps(n[i]) for i in 0..<n.safeLen: deps(n[i])
of nkMixinStmt, nkBindStmt: discard
else: else:
for i in 0..<n.safeLen: deps(n[i]) for i in 0..<n.safeLen: deps(n[i])

View file

@ -42,6 +42,7 @@ type
mappingExists*: bool mappingExists*: bool
mapping*: TIdTable mapping*: TIdTable
caseContext*: seq[tuple[n: PNode, idx: int]] caseContext*: seq[tuple[n: PNode, idx: int]]
localBindStmts*: seq[PNode]
TMatchedConcept* = object TMatchedConcept* = object
candidateType*: PType candidateType*: PType

View file

@ -2954,6 +2954,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
for i in 0..<n.len: for i in 0..<n.len:
n[i] = semExpr(c, n[i]) n[i] = semExpr(c, n[i])
of nkComesFrom: discard "ignore the comes from information for now" of nkComesFrom: discard "ignore the comes from information for now"
of nkMixinStmt: discard
of nkBindStmt:
if c.p != nil:
c.p.localBindStmts.add n
else:
localError(c.config, n.info, "invalid context for 'bind' statement: " &
renderTree(n, {renderNoComments}))
else: else:
localError(c.config, n.info, "invalid expression: " & localError(c.config, n.info, "invalid expression: " &
renderTree(n, {renderNoComments})) renderTree(n, {renderNoComments}))

View file

@ -318,6 +318,14 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
prc.typ = result prc.typ = result
popInfoContext(c.config) popInfoContext(c.config)
proc fillMixinScope(c: PContext) =
var p = c.p
while p != nil:
for bnd in p.localBindStmts:
for n in bnd:
addSym(c.currentScope, n.sym)
p = p.next
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
info: TLineInfo): PSym {.nosinks.} = info: TLineInfo): PSym {.nosinks.} =
## Generates a new instance of a generic procedure. ## Generates a new instance of a generic procedure.
@ -344,6 +352,10 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
result.ast = n result.ast = n
pushOwner(c, result) pushOwner(c, result)
# mixin scope:
openScope(c)
fillMixinScope(c)
openScope(c) openScope(c)
let gp = n[genericParamsPos] let gp = n[genericParamsPos]
internalAssert c.config, gp.kind != nkEmpty internalAssert c.config, gp.kind != nkEmpty
@ -394,6 +406,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
popProcCon(c) popProcCon(c)
popInfoContext(c.config) popInfoContext(c.config)
closeScope(c) # close scope for parameters closeScope(c) # close scope for parameters
closeScope(c) # close scope for 'mixin' declarations
popOwner(c) popOwner(c)
c.currentScope = oldScope c.currentScope = oldScope
discard c.friendModules.pop() discard c.friendModules.pop()

View file

@ -391,7 +391,8 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
addFactNeg(c.guards, canon(n[0], c.graph.operators)) addFactNeg(c.guards, canon(n[0], c.graph.operators))
dec c.inLoop dec c.inLoop
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef: nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef,
nkMixinStmt, nkBindStmt, nkExportStmt:
discard discard
else: else:
analyseSons(c, n) analyseSons(c, n)

View file

@ -86,6 +86,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
a = nextOverloadIter(o, c, n) a = nextOverloadIter(o, c, n)
proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
result = copyNode(n)
for i in 0..<n.len: for i in 0..<n.len:
var a = n[i] var a = n[i]
# If 'a' is an overloaded symbol, we used to use the first symbol # If 'a' is an overloaded symbol, we used to use the first symbol
@ -99,16 +100,19 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
let sc = symChoice(c, n, s, scClosed) let sc = symChoice(c, n, s, scClosed)
if sc.kind == nkSym: if sc.kind == nkSym:
toBind.incl(sc.sym.id) toBind.incl(sc.sym.id)
result.add sc
else: else:
for x in items(sc): toBind.incl(x.sym.id) for x in items(sc):
toBind.incl(x.sym.id)
result.add x
else: else:
illFormedAst(a, c.config) illFormedAst(a, c.config)
result = newNodeI(nkEmpty, n.info)
proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode = proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
result = copyNode(n)
for i in 0..<n.len: for i in 0..<n.len:
toMixin.incl(considerQuotedIdent(c, n[i]).id) toMixin.incl(considerQuotedIdent(c, n[i]).id)
result = newNodeI(nkEmpty, n.info) result.add symChoice(c, n[i], nil, scForceOpen)
proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) = proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
case n.kind case n.kind

View file

@ -971,7 +971,7 @@ proc transform(c: PTransf, n: PNode): PNode =
of nkConstSection: of nkConstSection:
# do not replace ``const c = 3`` with ``const 3 = 3`` # do not replace ``const c = 3`` with ``const 3 = 3``
return transformConstSection(c, n) return transformConstSection(c, n)
of nkTypeSection, nkTypeOfExpr: of nkTypeSection, nkTypeOfExpr, nkMixinStmt, nkBindStmt:
# no need to transform type sections: # no need to transform type sections:
return n return n
of nkVarSection, nkLetSection: of nkVarSection, nkLetSection:

View file

@ -631,7 +631,8 @@ const
nkTypeSection, nkProcDef, nkConverterDef, nkTypeSection, nkProcDef, nkConverterDef,
nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr} nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
nkTypeOfExpr, nkMixinStmt, nkBindStmt}
proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) = proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) =
if constParameters in c.goals and tfNoSideEffect in callee.flags: if constParameters in c.goals and tfNoSideEffect in callee.flags:

View file

@ -2118,7 +2118,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
else: else:
dest = tmp0 dest = tmp0
of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma, of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt: nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
nkMixinStmt, nkBindStmt:
unused(c, n, dest) unused(c, n, dest)
of nkStringToCString, nkCStringToString: of nkStringToCString, nkCStringToString:
gen(c, n[0], dest) gen(c, n[0], dest)

View file

@ -5118,6 +5118,50 @@ scope is the default.
``bind`` statements only make sense in templates and generics. ``bind`` statements only make sense in templates and generics.
Delegating bind statements
--------------------------
The following example outlines a problem that can arise when generic
instantiations cross multiple different modules:
.. code-block:: nim
# module A
proc genericA*[T](x: T) =
mixin init
init(x)
.. code-block:: nim
import C
# module B
proc genericB*[T](x: T) =
# Without the `bind init` statement C's init proc is
# not available when `genericB` is instantiated:
bind init
genericA(x)
.. code-block:: nim
# module C
type O = object
proc init*(x: var O) = discard
.. code-block:: nim
# module main
import B, C
genericB O()
In module B has an `init` proc from module C in its scope that is not
taken into account when `genericB` is instantiated which leads to the
instantiation of `genericA`. The solution is to `forward`:idx these
symbols by a `bind` statement inside `genericB`.
Templates Templates
========= =========

View file

@ -0,0 +1,6 @@
proc libraryFunc*[T](x: T) =
mixin mixedIn, indirectlyMixedIn
echo mixedIn()
echo indirectlyMixedIn()

View file

@ -0,0 +1,3 @@
proc indirectlyMixedIn*: int =
200

View file

@ -0,0 +1,12 @@
import
generic_library, helper_module
proc mixedIn: int = 100
proc makeUseOfLibrary*[T](x: T) =
bind mixedIn, indirectlyMixedIn
libraryFunc(x)
when isMainModule:
makeUseOfLibrary "test"

9
tests/sandwich/tmain.nim Normal file
View file

@ -0,0 +1,9 @@
discard """
output: '''100
200'''
"""
import
module_using_generic_library
makeUseOfLibrary "test"