summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2021-03-09 20:19:24 +0100
committerGitHub <noreply@github.com>2021-03-09 20:19:24 +0100
commit2f213db7eed7fb715b803559b38af4148cff02e1 (patch)
tree916192dfea950705bad38e0442faf593cd5d5227
parent083eeda08503d0bf6b96efaab587b8ba2dce20a6 (diff)
downloadNim-2f213db7eed7fb715b803559b38af4148cff02e1.tar.gz
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'
-rw-r--r--compiler/ccgexprs.nim1
-rw-r--r--compiler/cgen.nim3
-rw-r--r--compiler/closureiters.nim2
-rw-r--r--compiler/injectdestructors.nim3
-rw-r--r--compiler/jsgen.nim3
-rw-r--r--compiler/lambdalifting.nim5
-rw-r--r--compiler/liftlocals.nim2
-rw-r--r--compiler/nilcheck.nim3
-rw-r--r--compiler/optimizer.nim6
-rw-r--r--compiler/reorder.nim1
-rw-r--r--compiler/semdata.nim1
-rw-r--r--compiler/semexprs.nim11
-rw-r--r--compiler/seminst.nim13
-rw-r--r--compiler/semparallel.nim3
-rw-r--r--compiler/semstmts.nim4
-rw-r--r--compiler/semtempl.nim10
-rw-r--r--compiler/transf.nim2
-rw-r--r--compiler/varpartitions.nim3
-rw-r--r--compiler/vmgen.nim3
-rw-r--r--doc/manual.rst44
-rw-r--r--tests/sandwich/generic_library.nim6
-rw-r--r--tests/sandwich/helper_module.nim3
-rw-r--r--tests/sandwich/module_using_generic_library.nim12
-rw-r--r--tests/sandwich/tmain.nim9
24 files changed, 132 insertions, 21 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index e30397107..2a8af5c41 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -2909,6 +2909,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     inc p.splitDecls
     genGotoState(p, n)
   of nkBreakState: genBreakState(p, n, d)
+  of nkMixinStmt, nkBindStmt: discard
   else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")
 
 proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope =
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index f87082866..5de23649f 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -860,7 +860,8 @@ proc containsResult(n: PNode): bool =
     for i in 0..<n.safeLen:
       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
 
 proc easyResultAsgn(n: PNode): PNode =
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index 2270797ea..e474e5ba2 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -157,7 +157,7 @@ type
 
 const
   nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
-            nkCommentStmt} + procDefs
+            nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
 
 proc newStateAccess(ctx: var Ctx): PNode =
   if ctx.stateVarSym.isNil:
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index 8cfd14cc4..6010ba43d 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -884,7 +884,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
     of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
        nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
        nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
-       nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr:
+       nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
+       nkTypeOfExpr, nkMixinStmt, nkBindStmt:
       result = n
 
     of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock:
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 0e71162d7..3fc7708bf 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -2626,7 +2626,8 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection, nkCommentStmt, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt,
+     nkMixinStmt, nkBindStmt: discard
   of nkIteratorDef:
     if n[0].sym.typ.callConv == TCallingConvention.ccClosure:
       globalError(p.config, n.info, "Closure iterators are not supported by JS backend!")
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 8eaa9e1e2..6f43649a1 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -497,7 +497,8 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
           w = up
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit,
      nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef,
-     nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt, nkTypeOfExpr:
+     nkConverterDef, nkMacroDef, nkFuncDef, nkCommentStmt,
+     nkTypeOfExpr, nkMixinStmt, nkBindStmt:
     discard
   of nkLambdaKinds, nkIteratorDef:
     if n.typ != nil:
@@ -752,7 +753,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
         result = accessViaEnvVar(n, owner, d, c)
   of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkComesFrom,
      nkTemplateDef, nkTypeSection, nkProcDef, nkMethodDef, nkConverterDef,
-     nkMacroDef, nkFuncDef:
+     nkMacroDef, nkFuncDef, nkMixinStmt, nkBindStmt:
     discard
   of nkClosure:
     if n[1].kind == nkNilLit:
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index 0b46c73a2..7ca46ab1b 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -43,7 +43,7 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) =
   of nkSym:
     if interestingVar(it.sym):
       n[i] = lookupOrAdd(c, it.sym, it.info)
-  of procDefs, nkTypeSection: discard
+  of procDefs, nkTypeSection, nkMixinStmt, nkBindStmt: discard
   else:
     for i in 0..<it.safeLen:
       liftLocals(it, i, c)
diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim
index d0ef45d04..b779830d6 100644
--- a/compiler/nilcheck.nim
+++ b/compiler/nilcheck.nim
@@ -1260,7 +1260,8 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
   of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
       nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
       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"
     result = Check(nilability: Nil, map: map)
diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim
index 5d1139bfd..744c82ab5 100644
--- a/compiler/optimizer.nim
+++ b/compiler/optimizer.nim
@@ -150,7 +150,8 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
   of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
       nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
       nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
-      nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr:
+      nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
+      nkTypeOfExpr, nkMixinStmt, nkBindStmt:
     discard "do not follow the construct"
 
   of nkAsgn, nkFastAsgn:
@@ -249,7 +250,8 @@ proc opt(c: Con; n, parent: PNode; parentPos: int) =
   of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef,
       nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
       nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
-      nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr:
+      nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr,
+      nkMixinStmt, nkBindStmt:
     parent[parentPos] = n
 
   else:
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index 3eb47941e..d2b89f392 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -105,6 +105,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
       decl(a[1])
     else:
       for i in 0..<n.safeLen: deps(n[i])
+  of nkMixinStmt, nkBindStmt: discard
   else:
     for i in 0..<n.safeLen: deps(n[i])
 
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 174d6a0b2..8c65939d4 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -42,6 +42,7 @@ type
     mappingExists*: bool
     mapping*: TIdTable
     caseContext*: seq[tuple[n: PNode, idx: int]]
+    localBindStmts*: seq[PNode]
 
   TMatchedConcept* = object
     candidateType*: PType
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 41f0ef48a..a7cc235c0 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -75,7 +75,7 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode =
     # bug #12741, redundant error messages are the lesser evil here:
     localError(c.config, n.info, errExprXHasNoType %
                 renderTree(result, {renderNoComments}))
-  
+
   if isEmpty:
     # do not produce another redundant error message:
     result = errorNode(c, n)
@@ -2021,7 +2021,7 @@ proc processQuotations(c: PContext; n: var PNode, op: string,
     n = newIdentNode(getIdent(c.cache, $quotes.len), n.info)
     ids.add n
     return
-  
+
   template handlePrefixOp(prefixed) =
     if prefixed[0].kind == nkIdent:
       let examinedOp = prefixed[0].ident.s
@@ -2954,6 +2954,13 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     for i in 0..<n.len:
       n[i] = semExpr(c, n[i])
   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:
     localError(c.config, n.info, "invalid expression: " &
                renderTree(n, {renderNoComments}))
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 845929648..62cbdbcc1 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -318,6 +318,14 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   prc.typ = result
   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,
                       info: TLineInfo): PSym {.nosinks.} =
   ## Generates a new instance of a generic procedure.
@@ -344,6 +352,10 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   result.ast = n
   pushOwner(c, result)
 
+  # mixin scope:
+  openScope(c)
+  fillMixinScope(c)
+
   openScope(c)
   let gp = n[genericParamsPos]
   internalAssert c.config, gp.kind != nkEmpty
@@ -394,6 +406,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   popProcCon(c)
   popInfoContext(c.config)
   closeScope(c)           # close scope for parameters
+  closeScope(c)           # close scope for 'mixin' declarations
   popOwner(c)
   c.currentScope = oldScope
   discard c.friendModules.pop()
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index d0fc329a0..1dc9a1dfd 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -391,7 +391,8 @@ proc analyse(c: var AnalysisCtx; n: PNode) =
         addFactNeg(c.guards, canon(n[0], c.graph.operators))
     dec c.inLoop
   of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,
-      nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef:
+      nkMacroDef, nkTemplateDef, nkConstSection, nkPragma, nkFuncDef,
+      nkMixinStmt, nkBindStmt, nkExportStmt:
     discard
   else:
     analyseSons(c, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 47d9061e6..1fe540a65 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1823,8 +1823,8 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
       let t = tt[col]
       if t != nil and t.kind == tyGenericInvocation:
         var x = skipTypes(t[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst,
-                                      tyGenericInvocation, tyGenericBody,
-                                      tyAlias, tySink, tyOwned})
+                                 tyGenericInvocation, tyGenericBody,
+                                 tyAlias, tySink, tyOwned})
         if x.kind == tyObject and t.len-1 == n[genericParamsPos].len:
           foundObj = true
           addMethodToGeneric(c.graph, c.module.position, x, col, s)
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 1e6cd88b7..2f87fb8f2 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -86,6 +86,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
       a = nextOverloadIter(o, c, n)
 
 proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
+  result = copyNode(n)
   for i in 0..<n.len:
     var a = n[i]
     # 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)
       if sc.kind == nkSym:
         toBind.incl(sc.sym.id)
+        result.add sc
       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:
       illFormedAst(a, c.config)
-  result = newNodeI(nkEmpty, n.info)
 
 proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
+  result = copyNode(n)
   for i in 0..<n.len:
     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) =
   case n.kind
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 4e83ec1df..d2d9156aa 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -971,7 +971,7 @@ proc transform(c: PTransf, n: PNode): PNode =
   of nkConstSection:
     # do not replace ``const c = 3`` with ``const 3 = 3``
     return transformConstSection(c, n)
-  of nkTypeSection, nkTypeOfExpr:
+  of nkTypeSection, nkTypeOfExpr, nkMixinStmt, nkBindStmt:
     # no need to transform type sections:
     return n
   of nkVarSection, nkLetSection:
diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim
index 3093fb38f..7bb626c0a 100644
--- a/compiler/varpartitions.nim
+++ b/compiler/varpartitions.nim
@@ -631,7 +631,8 @@ const
     nkTypeSection, nkProcDef, nkConverterDef,
     nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo,
     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) =
   if constParameters in c.goals and tfNoSideEffect in callee.flags:
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index a1fcf5a8a..392fe9737 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -2118,7 +2118,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     else:
       dest = tmp0
   of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
-     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt:
+     nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
+     nkMixinStmt, nkBindStmt:
     unused(c, n, dest)
   of nkStringToCString, nkCStringToString:
     gen(c, n[0], dest)
diff --git a/doc/manual.rst b/doc/manual.rst
index 4884db0e1..ba7ef5059 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -5118,6 +5118,50 @@ scope is the default.
 ``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
 =========
 
diff --git a/tests/sandwich/generic_library.nim b/tests/sandwich/generic_library.nim
new file mode 100644
index 000000000..43e7bd65f
--- /dev/null
+++ b/tests/sandwich/generic_library.nim
@@ -0,0 +1,6 @@
+
+proc libraryFunc*[T](x: T) =
+  mixin mixedIn, indirectlyMixedIn
+  echo mixedIn()
+  echo indirectlyMixedIn()
+
diff --git a/tests/sandwich/helper_module.nim b/tests/sandwich/helper_module.nim
new file mode 100644
index 000000000..d003bf044
--- /dev/null
+++ b/tests/sandwich/helper_module.nim
@@ -0,0 +1,3 @@
+
+proc indirectlyMixedIn*: int =
+  200
diff --git a/tests/sandwich/module_using_generic_library.nim b/tests/sandwich/module_using_generic_library.nim
new file mode 100644
index 000000000..bbb0d92a4
--- /dev/null
+++ b/tests/sandwich/module_using_generic_library.nim
@@ -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"
diff --git a/tests/sandwich/tmain.nim b/tests/sandwich/tmain.nim
new file mode 100644
index 000000000..aa50bfb04
--- /dev/null
+++ b/tests/sandwich/tmain.nim
@@ -0,0 +1,9 @@
+discard """
+  output: '''100
+200'''
+"""
+
+import
+  module_using_generic_library
+
+makeUseOfLibrary "test"