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.21k stars 1.46k forks source link

`hasCustomPragma` does not work on generic types #23713

Open etan-status opened 1 week ago

etan-status commented 1 week ago

Description

import std/macros

template p {.pragma.}

type
  X {.p.} = object

  Y[T] = object
    t: T

echo Y[X].T
echo Y[X].T.hasCustomPragma(p)

Nim Version

% nim -v Nim Compiler Version 1.6.20 [MacOSX: amd64] Compiled at 2024-06-08 Copyright (c) 2006-2023 by Andreas Rumpf

git hash: 19fdbfc173bfccb64cb64e0a963e69f52f71fc73 active boot switches: -d:release

Current Output

/Nim/lib/core/macros.nim(1550, 18) Error: cannot get child of node kind: nnkSym

Expected Output

true

Possible Solution

Use typeof(default(Y[X]).t).hasCustomPragma(p) instead. But this is not always feasible as the inner t may not be exported in all situations.

Additional Information

Possibly related to https://github.com/nim-lang/Nim/issues/23564

metagn commented 1 week ago

The type of Y[X].T is typedesc[X], but it also stores the info of the symbol T of the original unresolved generic param and this is what getTypeInst gives. This symbol is given to the type here and here (with linkTo).

From what I understand this shouldn't happen, a typedesc type should not have a bound symbol unless it is literally typedesc. But I don't know which makes the most sense to do:

  1. Remove the linkTo lines in readTypeParameter and just return an nkType node; this might lose information or break stuff
  2. To the and t.sym != nil condition in mapTypeToAstX, add and t.kind != tyTypeDesc; this seems too specific
  3. To fix #23564 as well we can add and t.kind notin {tyTypeDesc, tyAlias} instead. That is, the only types where an "instantiation symbol" is ignored in favor of the implementation become typedescs or primitive alias types like type T = X. This has the caveat of defining new behavior for getTypeInst that might be incompatible with old behavior. We could also combine the first option with and t.kind != tyAlias instead but this seems more independent of compiler internals.

Edit: Actually I think hasCustomPragma should deal with tyAlias, getTypeInst shouldn't skip it.