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 1 commit
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 @@ -2906,6 +2906,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
inc p.splitDecls
genGotoState(p, n)
of nkBreakState: genBreakState(p, n, d)
of nkMixinStmt: discard
else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind")

proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope =
Expand Down
2 changes: 1 addition & 1 deletion compiler/cgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,7 @@ 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} +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't nkMixinStmt be stripped away in semantic pass before it reaches backend, eg, so that it doesn't affect each backend

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, but I'm generally in favor of not losing information in the AST. As you can see there are always things that backends explicitly need to ignore anyway.

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} + procDefs

proc newStateAccess(ctx: var Ctx): PNode =
if ctx.stateVarSym.isNil:
Expand Down
2 changes: 1 addition & 1 deletion compiler/injectdestructors.nim
Original file line number Diff line number Diff line change
Expand Up @@ -890,7 +890,7 @@ 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:
result = n

of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock:
Expand Down
2 changes: 1 addition & 1 deletion compiler/jsgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2626,7 +2626,7 @@ 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: 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
4 changes: 2 additions & 2 deletions compiler/lambdalifting.nim
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ 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:
discard
of nkLambdaKinds, nkIteratorDef:
if n.typ != nil:
Expand Down Expand Up @@ -752,7 +752,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:
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: discard
else:
for i in 0..<it.safeLen:
liftLocals(it, i, c)
Expand Down
2 changes: 1 addition & 1 deletion compiler/nilcheck.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1260,7 +1260,7 @@ 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:

discard "don't follow this : same as varpartitions"
result = Check(nilability: Nil, map: map)
Expand Down
2 changes: 1 addition & 1 deletion compiler/optimizer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ 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:
discard "do not follow the construct"

of nkAsgn, nkFastAsgn:
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: 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]]
localMixinStmts*: seq[PNode]

TMatchedConcept* = object
candidateType*: PType
Expand Down
8 changes: 7 additions & 1 deletion 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 @@ -2944,6 +2944,12 @@ 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:
if c.p != nil:
c.p.localMixinStmts.add n
else:
localError(c.config, n.info, "invalid context for 'mixin' statement: " &
renderTree(n, {renderNoComments}))
else:
localError(c.config, n.info, "invalid expression: " &
renderTree(n, {renderNoComments}))
Expand Down
14 changes: 14 additions & 0 deletions compiler/seminst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,15 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
prc.typ = result
popInfoContext(c.config)

proc fillMixinScope(c: PContext) =
var p = c.p
while p != nil:
for mix in p.localMixinStmts:
for choice in mix:
for n in choice:
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 +353,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 +407,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, 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
3 changes: 2 additions & 1 deletion compiler/semtempl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,10 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
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:
# no need to transform type sections:
return n
of nkVarSection, nkLetSection:
Expand Down
2 changes: 1 addition & 1 deletion compiler/varpartitions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ 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}

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 @@ -2116,7 +2116,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:
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 mixin statements
Copy link
Member

@timotheecour timotheecour Mar 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

---------------------------

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 `mixin init` statement C's init proc is
# not available when `genericB` is instantiated:
mixin 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 `mixin` 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) =
mixin 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"