I learned while reporting another issue probably related issue <https://github.com/nim-lang/Nim/issues/13061> that generic procs aren't type checked until they are used. As an example this proc on it's own typechecks but fails to typecheck if there is any concrete usage:
proc f[T](a:T):seq[T] = "not a seq"
Is this a deliberate design decision?
This is a side effect of how generics are implemented.
I don't know the exact details, but an obvious implementation would be to just store the AST of the generic proc until it needs to be instantiated; later, when the proc is called, we know the generic params passed to the proc, so we can easily inject the passed params into the current scope as typedescs. You can imagine something like this happening:
# our proc is this:
proc f[T](a: T): T =
result = a
# the following statement might yield in this
echo f[int](1)
# because f[int] wasn't instantiated yet, we declare it.
# keep in mind that this proc is not visible to the user.
block:
type T = int # the passed generic param, now as a typedesc
# this proc actually gets semchecked, instead of the original generic proc
proc f_int(a: T): T =
result = a
# the generic proc is "substituted"
echo f_int(1)
Keep in mind that the above explanation is an oversimplification.
This was probably a sane design decision. Consider this example:
proc add[T](a, b: T): T = a + b
When the semchecker checks the proc, how does it know if what's done is legit? Some person may pass a seq here, which would obviously not work. Checking all the generic types, etc. right where the proc is declared would only add to the complexity of the compiler. Simply instantiating the proc when needed and checking it then is a much simpler and much faster approach.
To my mind your example should not typecheck because it is making assumptions about the type T which should be opaque. I can see this working in a template:
template add (a,b:untyped):untyped = a + b
and if Nim wanted to lean on static introspection to discharge proofs in a dependent-typey kind of way something this may work:
proc add[T](a,b:T):T =
when T is int:
a + b
elif compiles(a+b):
a + b
else:
somethingAbsurd
Besides that the only other way I can think of to reason about generics is Araq's RFC which constrains the generic argument using concepts.
IMO all of those are better because the current situation.