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

Macro fails with `got: <S> but expected: <T>`. #19542

Open tsoj opened 2 years ago

tsoj commented 2 years ago

A macro generating a function fails with got: <S> but expected: <T>.

Example

import macros

type MyModel[T] = object
    discard

macro network*(): untyped =

    let underlyingTypeSymbol = ident"S"

    var params = @[
        newNimNode(nnkBracketExpr).add(
            ident"MyModel",
            underlyingTypeSymbol
        ),
        newIdentDefs(
            ident"a",
            newNimNode(nnkBracketExpr).add(
                ident"MyModel",
                underlyingTypeSymbol
            )
        )
    ]

    result = newProc(
        name = ident"init",
        params = params
    )
    # GenericParams
    result[2] = newNimNode(nnkGenericParams).add(
        newIdentDefs(
            underlyingTypeSymbol,
            newEmptyNode()
        )
    )

    echo toStrLit(result)

network()

Current Output

Hint: used config file '/playground/nim/config/nim.cfg' [Conf]
Hint: used config file '/playground/nim/config/config.nims' [Conf]
...........................................................
proc init[S](a: MyModel[S]): MyModel[S] =

/usercode/in.nim(39, 8) template/generic instantiation of `network` from here
/usercode/in.nim(11, 19) Error: cannot instantiate MyModel [type declared in /usercode/in.nim(3, 6)]
got: <S>
but expected: <T>

Expected Output

It should compile.

Additional Information

$ nim -v
Nim Compiler Version 1.7.1 [Linux: amd64]
Compiled at 2022-02-15
Copyright (c) 2006-2022 by Andreas Rumpf

git hash: b2c5d7b4ff070abe2145e998d090fb15b4df522f
active boot switches: -d:release

Workaround

A workaround is to copy the offending NimNode at usage:

import macros

type MyModel[T] = object
    discard

macro network*(): untyped =

    let underlyingTypeSymbol = ident"S"

    var params = @[
        newNimNode(nnkBracketExpr).add(
            ident"MyModel",
            copy underlyingTypeSymbol
        ),
        newIdentDefs(
            ident"a",
            newNimNode(nnkBracketExpr).add(
                ident"MyModel",
                copy underlyingTypeSymbol
            )
        )
    ]

    result = newProc(
        name = ident"init",
        params = params
    )
    # GenericParams
    result[2] = newNimNode(nnkGenericParams).add(
        newIdentDefs(
            copy underlyingTypeSymbol,
            newEmptyNode()
        )
    )

    echo toStrLit(result)

network()
metagn commented 10 months ago

Probably related #19670

Edit: Doesn't seem so, #19670 works now but this doesn't, and #19670's problem is with subscript expressions in templates which aren't present here

Works if you generate a new ident symbol every time for underlyingTypeSymbol