Skip to content

Generics "sandwiched" between two modules don't mixin their scope symbols properly #11225

Closed
@zah

Description

@zah

This problem is a bit tricky to understand, so please read carefully.

Let's imagine we have a library featuring generic functions that mixes in some symbols from the caller scope:

generic_library.nim

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

This library is used from another module that provides the needed symbols. mixedIn is defined locally, while indirectlyMixedIn is imported from yet another module:

module_using_generic_library.nim

import
  generic_library, helper_module

proc mixedIn: int = 100

proc makeUseOfLibrary*[T](x: T) =
  libraryFunc(x)

when isMainModule:
  makeUseOfLibrary "test"

helper_module.nim

proc indirectlyMixedIn*: int =
  200

So far, so good. If we compile module_using_generic_library, it will produce the expected output.

But let's introduce another module that imports and uses module_using_generic_library:

main.nim

import
  module_using_generic_library

makeUseOfLibrary "test"

If we try to compile it, we'll get the following error:

module_using_generic_library.nim(4, 6) Hint: 'mixedIn' is declared but not used [XDeclaredButNotUsed]
main.nim(4, 18) template/generic instantiation of `makeUseOfLibrary` from here
module_using_generic_library.nim(7, 14) template/generic instantiation of `libraryFunc` from here
generic_library.nim(3, 8) Error: undeclared identifier: 'mixedIn'

Why did this happen?

Please notice that the makeUseOfLibrary proc in the module_using_generic_library module is also generic. It is instantiated in main, but ultimately it consumes another generic defined in generic_library. Its scope is "sandwiched" between the main scope and the inner-most generic_library scope. At the moment mixin fails to recognise this and attempts to look for symbols in the outer-most scope (the one of main). This is highly surprising behavior for the user, because if you examine the lexical scopes, libraryFunc seems instantiated properly in the sandwiched module.

We've hit this problem multiple times during the development of nimcrypto, chronicles and nim-serialization. I've been asked to explain it on multiple occasions by almost everyone on our team, so it's an ongoing source of confusion and wasted time even for Nim experts. For this reason, I'm assigning high priority to the issue.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions