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

Experimental typeImports #8660

Closed
wants to merge 2 commits into from
Closed
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
34 changes: 34 additions & 0 deletions compiler/importer.nim
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ proc rawImportSymbol(c: PContext, s: PSym) =
if s.kind == skConverter: addConverter(c, s)
if hasPattern(s): addPattern(c, s)

proc maybeImportForType(c: PContext, s: PSym, t: PType) =
## Imports a symbol only if it has a strict relation with a type:
##
## - callable signature refers to the type
##
var m: TCandidate
initCandidate(c, m, t)

case s.kind
of skProcKinds:
for tt in s.typ.sons.items:
#FIXME no idea why we get nulls here
if tt == nil:
continue

if typeRel(m, tt, t) >= isSubtype:
rawImportSymbol(c, s)
break
else:
discard

proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
let ident = lookups.considerQuotedIdent(c, n)
let s = strTableGet(fromMod.tab, ident)
Expand All @@ -88,6 +109,19 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym) =
if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
rawImportSymbol(c, e)
e = nextIdentIter(it, fromMod.tab)

of skType:
# import the type as expected
rawImportSymbol(c, s)

# types can bring attached their related symbols too
if typeImports in c.features:
var i: TTabIter
var ss = initTabIter(i, fromMod.tab)
while ss != nil:
maybeImportForType(c, ss, s.typ)
ss = nextIter(i, fromMod.tab)

else: rawImportSymbol(c, s)

proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) =
Expand Down
3 changes: 2 additions & 1 deletion compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ type
notnil,
dynamicBindSym,
forLoopMacros,
caseStmtMacros
caseStmtMacros,
typeImports

SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf
Expand Down
16 changes: 16 additions & 0 deletions doc/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6333,6 +6333,22 @@ It's also possible to use ``from module import nil`` if one wants to import
the module but wants to enforce fully qualified access to every symbol
in ``module``.

By using the experimental feature ``typeImports`` it's possible to bring any
callable that has an imported type on its signature bundled with it.

.. code-block:: nim
:test: "nim c $1"

{.experimental: "typeImports".}

from colors import Color, colBlue

# related procs and operators like `$` are implicitly imported
let darker = intensity(colBlue, 0.3)
echo $darker
# values are not implicitly imported, this would fail:
#echo $colRed


Export statement
~~~~~~~~~~~~~~~~
Expand Down
11 changes: 11 additions & 0 deletions tests/modules/ttypeimport.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
discard """
output: "#FFC0CB"
"""

{.experimental: "typeImports".}

from colors import Color

# to string and proc imported
let pink = rgb(255, 192, 203)
echo $pink
11 changes: 11 additions & 0 deletions tests/modules/ttypeimportfail.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
discard """
file: "ttypeimportfail.nim"
errormsg: "undeclared identifier: 'colBlue'"
"""

{.experimental: "typeImports".}

from colors import Color

# constants of the type not imported
discard colBlue
12 changes: 12 additions & 0 deletions tests/modules/ttypeimportgen.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
discard """
output: "10"
"""

{.experimental: "typeImports".}

from collections.deques import Deque

var dq = initDeque[int]()
dq.addLast(10)
assert len(dq) == 1
echo peekLast(dq)