nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.55k stars 1.47k forks source link

template lookup fails with nested generic types #9494

Open arnetheduck opened 6 years ago

arnetheduck commented 6 years ago

this should compile:

type
  X[T] = object
    inner: T

template nest(t: int): untyped = 42
template nest(t: X): untyped = nest(t.inner)

# uncomment to make one level of nesting work
# template nest(t: X[X]): untyped = nest(t.inner)

var tmp: X[X[int]]

echo nest(tmp)

instead, it gives:

nim c nested
Hint: used config file '/home/arnetheduck/status/Nim/config/nim.cfg' [Conf]
Hint: system [Processing]
Hint: nested [Processing]
nested.nim(13, 10) Error: type mismatch: got <X[system.int]>
but expected one of: 
template nest(t: int): untyped

expression: nest(tmp.inner)
 nim --version
Nim Compiler Version 0.19.0 [Linux: amd64]
Compiled at 2018-09-26
Copyright (c) 2006-2018 by Andreas Rumpf

git hash: 4b2f0035007d4060dd16279c8c5e49c0cfb3b8f7
active boot switches: -d:release
krux02 commented 5 years ago

I have a workaround with macros:


type
  X[T] = object
    inner: T

import macros

macro nest(arg: typed): untyped =
  if arg.getTypeInst == bindSym"int":
    result = newLit(42)
  else:
    result = quote do:
      nest(`arg`.inner)

var tmp: X[X[int]]

echo nest(tmp)

It seems the template doesn't take itself into consideration for symbol lookup. But that macro above does technically the same thing.

arnetheduck commented 5 years ago

yeah, macros obfuscate the intent of the code though - it can also be worked around by adding explicitly instantiated overloads, but neither is really a general / satisfactory solution

krux02 commented 5 years ago

The macro solution is a general solution. It is even more general than the template solution. But I agree that it would be better if overloading would be nicer. This workaround is more scaleable than the explicit overloads.

arnetheduck commented 5 years ago

ah, sorry, something I failed to mention is that nest is an extension point for an api, so it might come from any module, really, meaning we can't implement the macro in the suggested way - the bug was discovered while trying to move away from a macro to use a more natural feature of the language :)

jcosborn commented 5 years ago

Adding a mixin works. Otherwise, since there is only one previous definition of nest, the call gets bound to a closed symbol.

type
  X[T] = object
    inner: T

template nest(t: int): untyped = 42
template nest(t: X): untyped =
  mixin nest
  nest(t.inner)

var tmp: X[X[int]]

echo nest(tmp)
arnetheduck commented 5 years ago

@jcosborn perfect - works and expresses the intent clearly, thanks!

so.. is this just a poor error message / should the mixin be necessary for recursive calls?

jcosborn commented 5 years ago

The error message is correct, but if one isn't aware of the rule, it can be confusing. Adding a suggestion to try mixin if the symbol is closed could be useful.

I don't know if having the routine name symbol visible in the routine body is feasible, but it does seem like it would be useful.

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. If you think it is still a valid issue, write a comment below; otherwise it will be closed. Thank you for your contributions.

metagn commented 1 year ago
import macros

macro typedTree(foo: typed) =
  echo foo.treeRepr
  foo

type X[T] = object
  inner: T

template nest(t: int): untyped = 42
typedTree:
  template nest(t: X): untyped = nest(t.inner)

Gives:

StmtList
  TemplateDef
    Sym "nest"
    Empty
    GenericParams
      Sym "X"
    FormalParams
      Sym "untyped"
      IdentDefs
        Sym "t"
        Sym "X"
        Empty
    Empty
    Bracket
      Empty
      Empty
    StmtList
      Call
        Sym "nest"
        DotExpr
          Sym "t"
          Ident "inner"

Important part being Sym "nest". Template doesn't notice itself when getting a sym choice for nest. Maybe related: #12012

metagn commented 9 months ago

Also mixin nest is a workaround