Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 4 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/ccgexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
3 changes: 2 additions & 1 deletion compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
2 changes: 1 addition & 1 deletion compiler/closureiters.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion compiler/injectdestructors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion compiler/jsgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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!")
Expand Down
5 changes: 3 additions & 2 deletions compiler/lambdalifting.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion compiler/liftlocals.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion compiler/nilcheck.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 4 additions & 2 deletions compiler/optimizer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions compiler/reorder.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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])

Expand Down
1 change: 1 addition & 0 deletions compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type
mappingExists*: bool
mapping*: TIdTable
caseContext*: seq[tuple[n: PNode, idx: int]]
localBindStmts*: seq[PNode]

TMatchedConcept* = object
candidateType*: PType
Expand Down
11 changes: 9 additions & 2 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}))
Expand Down
13 changes: 13 additions & 0 deletions compiler/seminst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 2 additions & 1 deletion compiler/semparallel.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
10 changes: 7 additions & 3 deletions compiler/semtempl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler/transf.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion compiler/varpartitions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
44 changes: 44 additions & 0 deletions doc/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
=========

Expand Down
6 changes: 6 additions & 0 deletions tests/sandwich/generic_library.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

proc libraryFunc*[T](x: T) =
mixin mixedIn, indirectlyMixedIn
echo mixedIn()
echo indirectlyMixedIn()

3 changes: 3 additions & 0 deletions tests/sandwich/helper_module.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

proc indirectlyMixedIn*: int =
200
12 changes: 12 additions & 0 deletions tests/sandwich/module_using_generic_library.nim
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions tests/sandwich/tmain.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
discard """
output: '''100
200'''
"""

import
module_using_generic_library

makeUseOfLibrary "test"