Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid expected type, but got: Raising(Future[void], E) on macro returning type (regression from 1.6) #23432

Open
arnetheduck opened this issue Mar 21, 2024 · 10 comments · May be fixed by #23438

Comments

@arnetheduck
Copy link
Contributor

Description

import std/[sequtils, macros]

type
  Future[T] = object
  InternalRaisesFuture[T, E] = object

macro Raising*[T](F: typedesc[Future[T]], E: varargs[typedesc]): untyped =
  ## Given a Future type instance, return a type storing `{.raises.}`
  ## information
  ##
  ## Note; this type may change in the future
  E.expectKind(nnkBracket)

  let raises = nnkTupleConstr.newTree(E.mapIt(it))
  nnkBracketExpr.newTree(
    ident "InternalRaisesFuture",
    nnkDotExpr.newTree(F, ident"T"),
    raises
  )

type X[E] = Future[void].Raising(E)

proc f(x: X) = discard


var v: Future[void].Raising([ValueError])
f(v)

Nim Version

works in 1.6, broken in 2.0, devel

Current Output

testit.nim(21, 25) Error: expected type, but got: Raising(Future[void], E)

Expected Output

No response

Possible Solution

No response

Additional Information

No response

@metagn
Copy link
Collaborator

metagn commented Mar 21, 2024

Changing varargs[typedesc] to varargs[untyped] (and expecting nnkArgList and not nnkBracket) works, E probably doesn't match typedesc and something goes wrong trying to delay the macro evaluation. This is new behavior in 2.0, previously generic parameters matched every type including typedesc.

You probably don't want the macro evaluation delayed anyway because then we would have to explicitly instantiate X every time since we don't know anything about its structure to match it to anything. The error message is still weird.

This also works without the last 2 lines in the example which forces the delayed macro evaluation because of #23406:

type X[E] = Raising(Future[void], E)

But then actually calling f fails because delaying is bad, this also gives a worse error message.

The reason we don't want unresolved generic parameters to match typedesc is because of stuff like:

proc foo(x: typedesc): int =
  sizeof(x)

@arnetheduck
Copy link
Contributor Author

My expectation would have been that varargs[typedesc] would give a better error message if one does something like ...Raising([42]), a situation we want to avoid.

Btw, like so?

import std/[sequtils, macros]

type
  Future[T] = object
  InternalRaisesFuture[T, E] = object

macro Raising2*[T](F: typedesc[Future[T]], E: varargs[untyped]): untyped =
  E.expectKind(nnkArgList)

  let raises = nnkTupleConstr.newTree(E.mapIt(it))
  nnkBracketExpr.newTree(
    ident "InternalRaisesFuture",
    nnkDotExpr.newTree(F, ident"T"),
    raises
  )

type Abc = object
  field: Future[void].Raising2([ValueError])

gives:

testit.nim(46, 3) Error: '(array[0..0, typedesc[ValueError]],)' is not a concrete type

@metagn
Copy link
Collaborator

metagn commented Mar 21, 2024

testit.nim(46, 3) Error: '(array[0..0, typedesc[ValueError]],)' is not a concrete type

Because of [ValueError], removing the brackets works. Could also unwrap the bracket in the macro.

Also varargs[typed] instead of varargs[untyped] doesn't use nnkArgList but also doesn't unwrap the brackets.

@beef331
Copy link
Collaborator

beef331 commented Mar 21, 2024

A workaround could be using varargs[typed] and just checking the type of Nodes manually, like so:

import std/[sequtils, macros]

type
  Future[T] = object
  InternalRaisesFuture[T, E] = object

macro Raising*[T](F: typedesc[Future[T]], E: varargs[typed]): untyped =
  ## Given a Future type instance, return a type storing `{.raises.}`
  ## information
  ##
  ## Note; this type may change in the future
  E.expectKind(nnkBracket)
  
  for typ in E:
    if typ.getTypeInst.typeKind != ntyTypeDesc:
      error("Expected typedesc, but got: '" & typ.repr & "'.", typ)

  let raises = nnkTupleConstr.newTree(E.mapIt(it))
  nnkBracketExpr.newTree(
    ident "InternalRaisesFuture",
    nnkDotExpr.newTree(F, ident"T"),
    raises
  )

type X[E] = Future[void].Raising(E)

proc f(x: X) = discard


var v: Future[void].Raising(ValueError, 42)
f(v)

arnetheduck added a commit to status-im/nim-chronos that referenced this issue Mar 22, 2024
arnetheduck added a commit to status-im/nim-chronos that referenced this issue Mar 25, 2024
* make `Raising` compatible with 2.0

See nim-lang/Nim#23432

* Update tests/testfut.nim

* Update tests/testfut.nim
@arnetheduck
Copy link
Contributor Author

@beef331 thanks for the workaround, the final version ended up looking like this: status-im/nim-chronos#526 - there are some limitations but the outcome is more or less consistent with how it worked in 1.6, at least for the use cases we had already.

Of course, it's sad to have to manually implement a poor version of what the compiler should be doing already better - worth considering in the next round of macro changes ;)

@ringabout
Copy link
Member

!nim c

import std/[sequtils, macros]

type
  Future[T] = object
  InternalRaisesFuture[T, E] = object

macro Raising*[T](F: typedesc[Future[T]], E: varargs[typedesc]): untyped =
  ## Given a Future type instance, return a type storing `{.raises.}`
  ## information
  ##
  ## Note; this type may change in the future
  E.expectKind(nnkBracket)

  let raises = nnkTupleConstr.newTree(E.mapIt(it))
  nnkBracketExpr.newTree(
    ident "InternalRaisesFuture",
    nnkDotExpr.newTree(F, ident"T"),
    raises
  )

type X[E] = Future[void].Raising(E)

proc f(x: X) = discard


var v: Future[void].Raising([ValueError])
f(v)

Copy link
Contributor

🐧 Linux bisect by @ringabout (member)
devel 👎 FAIL

Output

Error: Command failed: nim c --run  -d:nimDebug -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim
/home/runner/work/Nim/Nim/temp.nim(17, 25) Error: expected type, but got: Raising(Future[void], E)
assertions.nim(34)       raiseAssert
Error: unhandled exception: errGenerated [AssertionDefect]

IR

Compiled filesize 0 bytes (0 bytes)

Stats

  • Started 2024-03-25T10:23:34
  • Finished 2024-03-25T10:23:34
  • Duration

AST

nnkStmtList.newTree(
  nnkImportStmt.newTree(
    nnkInfix.newTree(
      newIdentNode("/"),
      newIdentNode("std"),
      nnkBracket.newTree(
        newIdentNode("sequtils"),
        newIdentNode("macros")
      )
    )
  ),
  nnkTypeSection.newTree(
    nnkTypeDef.newTree(
      newIdentNode("Future"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("T"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkObjectTy.newTree(
        newEmptyNode(),
        newEmptyNode(),
        newEmptyNode()
      )
    ),
    nnkTypeDef.newTree(
      newIdentNode("InternalRaisesFuture"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("T"),
          newIdentNode("E"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkObjectTy.newTree(
        newEmptyNode(),
        newEmptyNode(),
        newEmptyNode()
      )
    )
  ),
  nnkMacroDef.newTree(
    nnkPostfix.newTree(
      newIdentNode("*"),
      newIdentNode("Raising")
    ),
    newEmptyNode(),
    nnkGenericParams.newTree(
      nnkIdentDefs.newTree(
        newIdentNode("T"),
        newEmptyNode(),
        newEmptyNode()
      )
    ),
    nnkFormalParams.newTree(
      newIdentNode("untyped"),
      nnkIdentDefs.newTree(
        newIdentNode("F"),
        nnkBracketExpr.newTree(
          newIdentNode("typedesc"),
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("T")
          )
        ),
        newEmptyNode()
      ),
      nnkIdentDefs.newTree(
        newIdentNode("E"),
        nnkBracketExpr.newTree(
          newIdentNode("varargs"),
          newIdentNode("typedesc")
        ),
        newEmptyNode()
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      newCommentStmtNode("Given a Future type instance, return a type storing `{.raises.}`\ninformation\n\nNote; this type may change in the future"),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          newIdentNode("E"),
          newIdentNode("expectKind")
        ),
        newIdentNode("nnkBracket")
      ),
      nnkLetSection.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("raises"),
          newEmptyNode(),
          nnkCall.newTree(
            nnkDotExpr.newTree(
              newIdentNode("nnkTupleConstr"),
              newIdentNode("newTree")
            ),
            nnkCall.newTree(
              nnkDotExpr.newTree(
                newIdentNode("E"),
                newIdentNode("mapIt")
              ),
              newIdentNode("it")
            )
          )
        )
      ),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          newIdentNode("nnkBracketExpr"),
          newIdentNode("newTree")
        ),
        nnkCommand.newTree(
          newIdentNode("ident"),
          newLit("InternalRaisesFuture")
        ),
        nnkCall.newTree(
          nnkDotExpr.newTree(
            newIdentNode("nnkDotExpr"),
            newIdentNode("newTree")
          ),
          newIdentNode("F"),
          nnkCallStrLit.newTree(
            newIdentNode("ident"),
            newLit("T")
          )
        ),
        newIdentNode("raises")
      )
    )
  ),
  nnkTypeSection.newTree(
    nnkTypeDef.newTree(
      newIdentNode("X"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("E"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("void")
          ),
          newIdentNode("Raising")
        ),
        newIdentNode("E")
      )
    )
  ),
  nnkProcDef.newTree(
    newIdentNode("f"),
    newEmptyNode(),
    newEmptyNode(),
    nnkFormalParams.newTree(
      newEmptyNode(),
      nnkIdentDefs.newTree(
        newIdentNode("x"),
        newIdentNode("X"),
        newEmptyNode()
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      nnkDiscardStmt.newTree(
        newEmptyNode()
      )
    )
  ),
  nnkVarSection.newTree(
    nnkIdentDefs.newTree(
      newIdentNode("v"),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("void")
          ),
          newIdentNode("Raising")
        ),
        nnkBracket.newTree(
          newIdentNode("ValueError")
        )
      ),
      newEmptyNode()
    )
  ),
  nnkCall.newTree(
    newIdentNode("f"),
    newIdentNode("v")
  )
)
stable 👎 FAIL

Output

Error: Command failed: nim c --run  -d:nimDebug -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim
/home/runner/work/Nim/Nim/temp.nim(17, 25) Error: expected type, but got: Raising(Future[void], E)
assertions.nim(34)       raiseAssert
Error: unhandled exception: options.nim(681, 5) `false` errGenerated [AssertionDefect]

IR

Compiled filesize 0 bytes (0 bytes)

Stats

  • Started 2024-03-25T10:23:35
  • Finished 2024-03-25T10:23:35
  • Duration

AST

nnkStmtList.newTree(
  nnkImportStmt.newTree(
    nnkInfix.newTree(
      newIdentNode("/"),
      newIdentNode("std"),
      nnkBracket.newTree(
        newIdentNode("sequtils"),
        newIdentNode("macros")
      )
    )
  ),
  nnkTypeSection.newTree(
    nnkTypeDef.newTree(
      newIdentNode("Future"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("T"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkObjectTy.newTree(
        newEmptyNode(),
        newEmptyNode(),
        newEmptyNode()
      )
    ),
    nnkTypeDef.newTree(
      newIdentNode("InternalRaisesFuture"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("T"),
          newIdentNode("E"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkObjectTy.newTree(
        newEmptyNode(),
        newEmptyNode(),
        newEmptyNode()
      )
    )
  ),
  nnkMacroDef.newTree(
    nnkPostfix.newTree(
      newIdentNode("*"),
      newIdentNode("Raising")
    ),
    newEmptyNode(),
    nnkGenericParams.newTree(
      nnkIdentDefs.newTree(
        newIdentNode("T"),
        newEmptyNode(),
        newEmptyNode()
      )
    ),
    nnkFormalParams.newTree(
      newIdentNode("untyped"),
      nnkIdentDefs.newTree(
        newIdentNode("F"),
        nnkBracketExpr.newTree(
          newIdentNode("typedesc"),
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("T")
          )
        ),
        newEmptyNode()
      ),
      nnkIdentDefs.newTree(
        newIdentNode("E"),
        nnkBracketExpr.newTree(
          newIdentNode("varargs"),
          newIdentNode("typedesc")
        ),
        newEmptyNode()
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      newCommentStmtNode("Given a Future type instance, return a type storing `{.raises.}`\ninformation\n\nNote; this type may change in the future"),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          newIdentNode("E"),
          newIdentNode("expectKind")
        ),
        newIdentNode("nnkBracket")
      ),
      nnkLetSection.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("raises"),
          newEmptyNode(),
          nnkCall.newTree(
            nnkDotExpr.newTree(
              newIdentNode("nnkTupleConstr"),
              newIdentNode("newTree")
            ),
            nnkCall.newTree(
              nnkDotExpr.newTree(
                newIdentNode("E"),
                newIdentNode("mapIt")
              ),
              newIdentNode("it")
            )
          )
        )
      ),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          newIdentNode("nnkBracketExpr"),
          newIdentNode("newTree")
        ),
        nnkCommand.newTree(
          newIdentNode("ident"),
          newLit("InternalRaisesFuture")
        ),
        nnkCall.newTree(
          nnkDotExpr.newTree(
            newIdentNode("nnkDotExpr"),
            newIdentNode("newTree")
          ),
          newIdentNode("F"),
          nnkCallStrLit.newTree(
            newIdentNode("ident"),
            newLit("T")
          )
        ),
        newIdentNode("raises")
      )
    )
  ),
  nnkTypeSection.newTree(
    nnkTypeDef.newTree(
      newIdentNode("X"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("E"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("void")
          ),
          newIdentNode("Raising")
        ),
        newIdentNode("E")
      )
    )
  ),
  nnkProcDef.newTree(
    newIdentNode("f"),
    newEmptyNode(),
    newEmptyNode(),
    nnkFormalParams.newTree(
      newEmptyNode(),
      nnkIdentDefs.newTree(
        newIdentNode("x"),
        newIdentNode("X"),
        newEmptyNode()
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      nnkDiscardStmt.newTree(
        newEmptyNode()
      )
    )
  ),
  nnkVarSection.newTree(
    nnkIdentDefs.newTree(
      newIdentNode("v"),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("void")
          ),
          newIdentNode("Raising")
        ),
        nnkBracket.newTree(
          newIdentNode("ValueError")
        )
      ),
      newEmptyNode()
    )
  ),
  nnkCall.newTree(
    newIdentNode("f"),
    newIdentNode("v")
  )
)
2.0.2 👎 FAIL

Output

Error: Command failed: nim c --run  -d:nimDebug -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim
/home/runner/work/Nim/Nim/temp.nim(17, 25) Error: expected type, but got: Raising(Future[void], E)
assertions.nim(34)       raiseAssert
Error: unhandled exception: options.nim(681, 5) `false` errGenerated [AssertionDefect]

IR

Compiled filesize 0 bytes (0 bytes)

Stats

  • Started 2024-03-25T10:23:35
  • Finished 2024-03-25T10:23:36
  • Duration

AST

nnkStmtList.newTree(
  nnkImportStmt.newTree(
    nnkInfix.newTree(
      newIdentNode("/"),
      newIdentNode("std"),
      nnkBracket.newTree(
        newIdentNode("sequtils"),
        newIdentNode("macros")
      )
    )
  ),
  nnkTypeSection.newTree(
    nnkTypeDef.newTree(
      newIdentNode("Future"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("T"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkObjectTy.newTree(
        newEmptyNode(),
        newEmptyNode(),
        newEmptyNode()
      )
    ),
    nnkTypeDef.newTree(
      newIdentNode("InternalRaisesFuture"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("T"),
          newIdentNode("E"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkObjectTy.newTree(
        newEmptyNode(),
        newEmptyNode(),
        newEmptyNode()
      )
    )
  ),
  nnkMacroDef.newTree(
    nnkPostfix.newTree(
      newIdentNode("*"),
      newIdentNode("Raising")
    ),
    newEmptyNode(),
    nnkGenericParams.newTree(
      nnkIdentDefs.newTree(
        newIdentNode("T"),
        newEmptyNode(),
        newEmptyNode()
      )
    ),
    nnkFormalParams.newTree(
      newIdentNode("untyped"),
      nnkIdentDefs.newTree(
        newIdentNode("F"),
        nnkBracketExpr.newTree(
          newIdentNode("typedesc"),
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("T")
          )
        ),
        newEmptyNode()
      ),
      nnkIdentDefs.newTree(
        newIdentNode("E"),
        nnkBracketExpr.newTree(
          newIdentNode("varargs"),
          newIdentNode("typedesc")
        ),
        newEmptyNode()
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      newCommentStmtNode("Given a Future type instance, return a type storing `{.raises.}`\ninformation\n\nNote; this type may change in the future"),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          newIdentNode("E"),
          newIdentNode("expectKind")
        ),
        newIdentNode("nnkBracket")
      ),
      nnkLetSection.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("raises"),
          newEmptyNode(),
          nnkCall.newTree(
            nnkDotExpr.newTree(
              newIdentNode("nnkTupleConstr"),
              newIdentNode("newTree")
            ),
            nnkCall.newTree(
              nnkDotExpr.newTree(
                newIdentNode("E"),
                newIdentNode("mapIt")
              ),
              newIdentNode("it")
            )
          )
        )
      ),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          newIdentNode("nnkBracketExpr"),
          newIdentNode("newTree")
        ),
        nnkCommand.newTree(
          newIdentNode("ident"),
          newLit("InternalRaisesFuture")
        ),
        nnkCall.newTree(
          nnkDotExpr.newTree(
            newIdentNode("nnkDotExpr"),
            newIdentNode("newTree")
          ),
          newIdentNode("F"),
          nnkCallStrLit.newTree(
            newIdentNode("ident"),
            newLit("T")
          )
        ),
        newIdentNode("raises")
      )
    )
  ),
  nnkTypeSection.newTree(
    nnkTypeDef.newTree(
      newIdentNode("X"),
      nnkGenericParams.newTree(
        nnkIdentDefs.newTree(
          newIdentNode("E"),
          newEmptyNode(),
          newEmptyNode()
        )
      ),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("void")
          ),
          newIdentNode("Raising")
        ),
        newIdentNode("E")
      )
    )
  ),
  nnkProcDef.newTree(
    newIdentNode("f"),
    newEmptyNode(),
    newEmptyNode(),
    nnkFormalParams.newTree(
      newEmptyNode(),
      nnkIdentDefs.newTree(
        newIdentNode("x"),
        newIdentNode("X"),
        newEmptyNode()
      )
    ),
    newEmptyNode(),
    newEmptyNode(),
    nnkStmtList.newTree(
      nnkDiscardStmt.newTree(
        newEmptyNode()
      )
    )
  ),
  nnkVarSection.newTree(
    nnkIdentDefs.newTree(
      newIdentNode("v"),
      nnkCall.newTree(
        nnkDotExpr.newTree(
          nnkBracketExpr.newTree(
            newIdentNode("Future"),
            newIdentNode("void")
          ),
          newIdentNode("Raising")
        ),
        nnkBracket.newTree(
          newIdentNode("ValueError")
        )
      ),
      newEmptyNode()
    )
  ),
  nnkCall.newTree(
    newIdentNode("f"),
    newIdentNode("v")
  )
)
1.6.14 👍 OK

Output


IR

Compiled filesize 95.41 Kb (97,696 bytes)
#define NIM_INTBITS 64
#include "nimbase.h"
#  define nimfr_(proc, file) \
 TFrame FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; nimFrame(&FR_);
#  define nimfrs_(proc, file, slots, length) \
 struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; nimFrame((TFrame*)&FR_);
#  define nimln_(n, file) \
 FR_.line = n; FR_.filename = file;
typedef struct tyObject_InternalRaisesFuture__joXl9brPglJfwBVacGXd5yg tyObject_InternalRaisesFuture__joXl9brPglJfwBVacGXd5yg;
struct tyObject_InternalRaisesFuture__joXl9brPglJfwBVacGXd5yg {
char dummy;
};
N_LIB_PRIVATE N_NIMCALL(void, f__temp_76)(tyObject_InternalRaisesFuture__joXl9brPglJfwBVacGXd5yg x);
static N_INLINE(void, nimFrame)(TFrame* s);
N_LIB_PRIVATE N_NOINLINE(void, callDepthLimitReached__system_2997)(void);
static N_INLINE(void, popFrame)(void);
static N_INLINE(void, initStackBottomWith)(void* locals);
N_LIB_PRIVATE N_NOINLINE(void, nimGC_setStackBottom)(void* theStackBottom);
N_LIB_PRIVATE N_NIMCALL(void, atmdotdotatsdotdotatsdotdotatsdotchoosenimatstoolchainsatsnimminus1dot6dot14atslibatssystemdotnim_DatInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, atmdotdotatsdotdotatsdotdotatsdotchoosenimatstoolchainsatsnimminus1dot6dot14atslibatssystemdotnim_Init000)(void);
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void);
N_LIB_PRIVATE tyObject_InternalRaisesFuture__joXl9brPglJfwBVacGXd5yg v__temp_75;
extern TFrame* framePtr__system_2564;
extern TFrame* framePtr__system_2564;
extern TFrame* framePtr__system_2564;
extern TFrame* framePtr__system_2564;
extern TFrame* framePtr__system_2564;
extern TFrame* framePtr__system_2564;
static N_INLINE(void, nimFrame)(TFrame* s) {
 {
  if (!(framePtr__system_2564 == ((TFrame*) NIM_NIL))) goto LA3_;
  (*s).calldepth = ((NI16) 0);
 }
 goto LA1_;
 LA3_: ;
 {
  (*s).calldepth = (NI16)((*framePtr__system_2564).calldepth + ((NI16) 1));
 }
 LA1_: ;
 (*s).prev = framePtr__system_2564;
 framePtr__system_2564 = s;
 {
  if (!((*s).calldepth == ((NI16) 2000))) goto LA8_;
  callDepthLimitReached__system_2997();
 }
 LA8_: ;
}
static N_INLINE(void, popFrame)(void) {
 framePtr__system_2564 = (*framePtr__system_2564).prev;
}
N_LIB_PRIVATE N_NIMCALL(void, f__temp_76)(tyObject_InternalRaisesFuture__joXl9brPglJfwBVacGXd5yg x) {
 nimfr_("f", "/home/runner/work/Nim/Nim/temp.nim");
 popFrame();
}
static N_INLINE(void, initStackBottomWith)(void* locals) {
 nimGC_setStackBottom(locals);
}
N_LIB_PRIVATE void PreMainInner(void) {
}
 N_LIB_PRIVATE int cmdCount;
 N_LIB_PRIVATE char** cmdLine;
 N_LIB_PRIVATE char** gEnv;
N_LIB_PRIVATE void PreMain(void) {
 void (*volatile inner)(void);
 inner = PreMainInner;
 atmdotdotatsdotdotatsdotdotatsdotchoosenimatstoolchainsatsnimminus1dot6dot14atslibatssystemdotnim_DatInit000();
 initStackBottomWith((void *)&inner);
 atmdotdotatsdotdotatsdotdotatsdotchoosenimatstoolchainsatsnimminus1dot6dot14atslibatssystemdotnim_Init000();
 (*inner)();
}
N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {
 NimMainModule();
}
N_CDECL(void, NimMain)(void) {
 void (*volatile inner)(void);
 PreMain();
 inner = NimMainInner;
 initStackBottomWith((void *)&inner);
 (*inner)();
}
int main(int argc, char** args, char** env) {
 cmdLine = args;
 cmdCount = argc;
 gEnv = env;
 NimMain();
 return nim_program_result;
}
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) {
{
 nimfr_("temp", "/home/runner/work/Nim/Nim/temp.nim");
 f__temp_76(v__temp_75);
 popFrame();
}
}

Stats

  • Started 2024-03-25T10:23:38
  • Finished 2024-03-25T10:23:39
  • Duration
1.4.8 👍 OK

Output


IR

Compiled filesize 91.30 Kb (93,496 bytes)
#define NIM_INTBITS 64
#include "nimbase.h"
#  define nimfr_(proc, file) \
 TFrame FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; nimFrame(&FR_);
#  define nimfrs_(proc, file, slots, length) \
 struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; nimFrame((TFrame*)&FR_);
#  define nimln_(n, file) \
 FR_.line = n; FR_.filename = file;
typedef struct tyObject_InternalRaisesFuture__SoZdjWYx5qf9aGd4M3pBJdg tyObject_InternalRaisesFuture__SoZdjWYx5qf9aGd4M3pBJdg;
struct tyObject_InternalRaisesFuture__SoZdjWYx5qf9aGd4M3pBJdg {
char dummy;
};
N_LIB_PRIVATE N_NIMCALL(void, f__Qr2Z0OouGgYw3QfhyalszA)(tyObject_InternalRaisesFuture__SoZdjWYx5qf9aGd4M3pBJdg x);
static N_INLINE(void, nimFrame)(TFrame* s);
N_LIB_PRIVATE N_NOINLINE(void, callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw)(void);
static N_INLINE(void, popFrame)(void);
static N_INLINE(void, initStackBottomWith)(void* locals);
N_LIB_PRIVATE N_NOINLINE(void, nimGC_setStackBottom)(void* theStackBottom);
N_LIB_PRIVATE N_NIMCALL(void, systemDatInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, systemInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void);
N_LIB_PRIVATE tyObject_InternalRaisesFuture__SoZdjWYx5qf9aGd4M3pBJdg v__ZBo9advJ7u8QecSF0Bd4bHA;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
static N_INLINE(void, nimFrame)(TFrame* s) {
 {
  if (!(framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw == ((TFrame*) NIM_NIL))) goto LA3_;
  (*s).calldepth = ((NI16) 0);
 }
 goto LA1_;
 LA3_: ;
 {
  (*s).calldepth = (NI16)((*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).calldepth + ((NI16) 1));
 }
 LA1_: ;
 (*s).prev = framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
 framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = s;
 {
  if (!((*s).calldepth == ((NI16) 2000))) goto LA8_;
  callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw();
 }
 LA8_: ;
}
static N_INLINE(void, popFrame)(void) {
 framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = (*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).prev;
}
N_LIB_PRIVATE N_NIMCALL(void, f__Qr2Z0OouGgYw3QfhyalszA)(tyObject_InternalRaisesFuture__SoZdjWYx5qf9aGd4M3pBJdg x) {
 nimfr_("f", "/home/runner/work/Nim/Nim/temp.nim");
 popFrame();
}
static N_INLINE(void, initStackBottomWith)(void* locals) {
 nimGC_setStackBottom(locals);
}
N_LIB_PRIVATE void PreMainInner(void) {
}
N_LIB_PRIVATE int cmdCount;
N_LIB_PRIVATE char** cmdLine;
N_LIB_PRIVATE char** gEnv;
N_LIB_PRIVATE void PreMain(void) {
 void (*volatile inner)(void);
 inner = PreMainInner;
 systemDatInit000();
 initStackBottomWith((void *)&inner);
 systemInit000();
 (*inner)();
}
N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {
 NimMainModule();
}
N_CDECL(void, NimMain)(void) {
 void (*volatile inner)(void);
 PreMain();
 inner = NimMainInner;
 initStackBottomWith((void *)&inner);
 (*inner)();
}
int main(int argc, char** args, char** env) {
 cmdLine = args;
 cmdCount = argc;
 gEnv = env;
 NimMain();
 return nim_program_result;
}
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) {
{
 nimfr_("temp", "/home/runner/work/Nim/Nim/temp.nim");
 f__Qr2Z0OouGgYw3QfhyalszA(v__ZBo9advJ7u8QecSF0Bd4bHA);
 popFrame();
}
}

Stats

  • Started 2024-03-25T10:23:42
  • Finished 2024-03-25T10:23:42
  • Duration
1.2.18 👍 OK

Output


IR

Compiled filesize 87.03 Kb (89,120 bytes)
#define NIM_INTBITS 64
#include "nimbase.h"
#  define nimfr_(proc, file) \
 TFrame FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; nimFrame(&FR_);
#  define nimfrs_(proc, file, slots, length) \
 struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; nimFrame((TFrame*)&FR_);
#  define nimln_(n, file) \
 FR_.line = n; FR_.filename = file;
typedef struct tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ;
struct tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ {
char dummy;
};
N_LIB_PRIVATE N_NIMCALL(void, f__7BqHdHV1GTsUzckBHACG6Q)(tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ x);
static N_INLINE(void, nimFrame)(TFrame* s);
N_LIB_PRIVATE N_NOINLINE(void, callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw)(void);
static N_INLINE(void, popFrame)(void);
static N_INLINE(void, initStackBottomWith)(void* locals);
N_LIB_PRIVATE N_NOINLINE(void, nimGC_setStackBottom)(void* theStackBottom);
N_LIB_PRIVATE N_NIMCALL(void, systemDatInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, systemInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void);
N_LIB_PRIVATE tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ v__ZBo9advJ7u8QecSF0Bd4bHA;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
static N_INLINE(void, nimFrame)(TFrame* s) {
 {
  if (!(framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw == NIM_NIL)) goto LA3_;
  (*s).calldepth = ((NI16) 0);
 }
 goto LA1_;
 LA3_: ;
 {
  (*s).calldepth = (NI16)((*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).calldepth + ((NI16) 1));
 }
 LA1_: ;
 (*s).prev = framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
 framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = s;
 {
  if (!((*s).calldepth == ((NI16) (((NI) 2000))))) goto LA8_;
  callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw();
 }
 LA8_: ;
}
static N_INLINE(void, popFrame)(void) {
 framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = (*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).prev;
}
N_LIB_PRIVATE N_NIMCALL(void, f__7BqHdHV1GTsUzckBHACG6Q)(tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ x) {
 nimfr_("f", "/home/runner/work/Nim/Nim/temp.nim");
 popFrame();
}
static N_INLINE(void, initStackBottomWith)(void* locals) {
 nimGC_setStackBottom(locals);
}
N_LIB_PRIVATE void PreMainInner(void) {
}
N_LIB_PRIVATE int cmdCount;
N_LIB_PRIVATE char** cmdLine;
N_LIB_PRIVATE char** gEnv;
N_LIB_PRIVATE void PreMain(void) {
 void (*volatile inner)(void);
 inner = PreMainInner;
 systemDatInit000();
 initStackBottomWith((void *)&inner);
 systemInit000();
 (*inner)();
}
N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {
 NimMainModule();
}
N_CDECL(void, NimMain)(void) {
 void (*volatile inner)(void);
 PreMain();
 inner = NimMainInner;
 initStackBottomWith((void *)&inner);
 (*inner)();
}
int main(int argc, char** args, char** env) {
 cmdLine = args;
 cmdCount = argc;
 gEnv = env;
 NimMain();
 return nim_program_result;
}
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) {
{
 nimfr_("temp", "/home/runner/work/Nim/Nim/temp.nim");
 f__7BqHdHV1GTsUzckBHACG6Q(v__ZBo9advJ7u8QecSF0Bd4bHA);
 popFrame();
}
}

Stats

  • Started 2024-03-25T10:23:44
  • Finished 2024-03-25T10:23:45
  • Duration
1.0.10 👍 OK

Output


IR

Compiled filesize 86.13 Kb (88,192 bytes)
#define NIM_INTBITS 64
#include "nimbase.h"
#  define nimfr_(proc, file) \
 TFrame FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; nimFrame(&FR_);
#  define nimfrs_(proc, file, slots, length) \
 struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
 FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; nimFrame((TFrame*)&FR_);
#  define nimln_(n, file) \
 FR_.line = n; FR_.filename = file;
typedef struct tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ;
struct tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ {
char dummy;
};
N_LIB_PRIVATE N_NIMCALL(void, f__7BqHdHV1GTsUzckBHACG6Q)(tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ x);
static N_INLINE(void, nimFrame)(TFrame* s);
N_LIB_PRIVATE N_NOINLINE(void, callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw)(void);
static N_INLINE(void, popFrame)(void);
static N_INLINE(void, initStackBottomWith)(void* locals);
N_NOINLINE(void, nimGC_setStackBottom)(void* theStackBottom);
N_LIB_PRIVATE N_NIMCALL(void, systemDatInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, systemInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void);
tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ v__ZBo9advJ7u8QecSF0Bd4bHA;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
extern TFrame* framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
static N_INLINE(void, nimFrame)(TFrame* s) {	NI T1_;
 T1_ = (NI)0;
 {
  if (!(framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw == NIM_NIL)) goto LA4_;
  T1_ = ((NI) 0);
 }
 goto LA2_;
 LA4_: ;
 {
  T1_ = ((NI) ((NI16)((*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).calldepth + ((NI16) 1))));
 }
 LA2_: ;
 (*s).calldepth = ((NI16) (T1_));
 (*s).prev = framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw;
 framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = s;
 {
  if (!((*s).calldepth == ((NI16) (((NI) 2000))))) goto LA9_;
  callDepthLimitReached__mMRdr4sgmnykA9aWeM9aDZlw();
 }
 LA9_: ;
}
static N_INLINE(void, popFrame)(void) {	framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw = (*framePtr__HRfVMH3jYeBJz6Q6X9b6Ptw).prev;
}
N_LIB_PRIVATE N_NIMCALL(void, f__7BqHdHV1GTsUzckBHACG6Q)(tyObject_InternalRaisesFuture__Er05yBj9atIFRAN6fCwoRwQ x) {	nimfr_("f", "/home/runner/work/Nim/Nim/temp.nim");
 popFrame();
}
static N_INLINE(void, initStackBottomWith)(void* locals) {	nimGC_setStackBottom(locals);
}
void PreMainInner(void) {
}
int cmdCount;
char** cmdLine;
char** gEnv;
void PreMain(void) {
 void (*volatile inner)(void);
 inner = PreMainInner;
 systemDatInit000();
 initStackBottomWith((void *)&inner);
 systemInit000();
 (*inner)();
}
N_CDECL(void, NimMainInner)(void) {
 NimMainModule();
}
N_CDECL(void, NimMain)(void) {
 void (*volatile inner)(void);
 PreMain();
 inner = NimMainInner;
 initStackBottomWith((void *)&inner);
 (*inner)();
}
int main(int argc, char** args, char** env) {
 cmdLine = args;
 cmdCount = argc;
 gEnv = env;
 NimMain();
 return nim_program_result;
}
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void) {
{
 nimfr_("temp", "/home/runner/work/Nim/Nim/temp.nim");
 f__7BqHdHV1GTsUzckBHACG6Q(v__ZBo9advJ7u8QecSF0Bd4bHA);
 popFrame();
}
}

Stats

  • Started 2024-03-25T10:23:46
  • Finished 2024-03-25T10:23:47
  • Duration
??? ➡️ 🐛

Diagnostics

The commit that introduced the bug can not be found, but the bug is in the commits:

(Can not find the commit because Nim can not be re-built commit-by-commit to bisect).

Stats
  • GCC 11.4.0
  • Clang 14.0.0
  • NodeJS 19.1
  • Created 2024-03-25T10:23:06Z
  • Comments 6
  • Commands nim c --run -d:nimDebug -d:nimDebugDlOpen -d:ssl -d:nimDisableCertificateValidation --forceBuild:on --colors:off --verbosity:0 --hints:off --lineTrace:off --nimcache:/home/runner/work/Nim/Nim --out:/home/runner/work/Nim/Nim/temp /home/runner/work/Nim/Nim/temp.nim

🤖 Bug found in 49 minutes bisecting 2147 commits at 43 commits per second

@metagn
Copy link
Collaborator

metagn commented Mar 25, 2024

Should have mentioned the origin is more than likely #22029

To be clear this isn't just a simple regression but needs a design choice:

  • There would be no problem if the macro was only called at instantiation time as a concrete type would match the typedesc param, but the macro has to be early evaluated here because otherwise we would have no information about the structure of X which is terrible for generic inference (I'm not even sure if this is implemented correctly).
  • But then the macro receives E as a typedesc param, which is normally illegal (i.e. a regular proc needs a concrete type for a typedesc param). So maybe we need to whitelist unresolved generic params (tyGenericParam) matching typedesc only for macros/templates. Would be an obscure rule but makes sense practically.

@arnetheduck
Copy link
Contributor Author

You probably don't want the macro evaluation delayed anyway because then we would have to explicitly instantiate X every time since we don't know anything about its structure to match it to anything.

I've been thinking about this, and I wonder if there's room for post-monomorphization macros somewhere in the language - when thinking about this problem, that's what I actually wanted.

Going further back in my reasoning around Raising, I actually wanted type classes (if I remember the nim name right) that understand inheritance, covariance and contravariance, because what we're actually trying to mimic here is the type arithmetic of {.raises.}, but early on, I couldn't get it to work for reasons I did not bother to investigate. I guess part of it is that the raises effect itself for some reason does not work with a type class.

metagn added a commit to metagn/Nim that referenced this issue Aug 17, 2024
@metagn
Copy link
Collaborator

metagn commented Aug 19, 2024

Sorry, after looking into it more it seems like some code even depends on typedesc not matching generic types early, there is this test for it and the union library seems to depend on it. The only reason #23438 passes CI is both these cases use normal call syntax which always delays the instantiation (see #23406, these were discovered trying to fix it in #23411).

I still understand the need for better type safety than just typed here, so maybe we could introduce a typeclass that matches expressions that have a typedesc type rather than just concrete typedescs, like typed{typedesc}. To also check if it's a fitting type for raises, we would need a constraint-checking mechanism that is delayed until instantiation unlike the expression-forming mechanism that is the macro. In this case a constraint on the parameter of InternalRaisesFuture should do it (ignoring current bugs with generic constraints) but I don't know if an intermediary generic type is always applicable.

metagn added a commit to metagn/Nim that referenced this issue Aug 19, 2024
metagn added a commit to metagn/Nim that referenced this issue Aug 20, 2024
Araq pushed a commit that referenced this issue Aug 20, 2024
fixes #23406, closes #23854, closes #23855 (test code of both compiles
but separate issue exists), refs #23432, follows #23411

In generic bodies, previously all regular `nkCall` nodes like `foo(a,
b)` were directly treated as generic statements and delayed immediately,
but other call kinds like `a.foo(b)`, `foo a, b` etc underwent
typechecking before making sure they have to be delayed, as implemented
in #22029. Since the behavior for `nkCall` was slightly buggy (as in
#23406), the behavior for all call kinds is now to call `semTypeExpr`.

However the vast majority of calls in generic bodies out there are
`nkCall`, and while there isn't a difference in the expected behavior,
this exposes many issues with the implementation started in #22029 given
how much more code uses it now. The portion of these issues that CI has
caught are fixed in this PR but it's possible there are more.

1. Deref expressions, dot expressions and calls to dot expressions now
handle and propagate `tyFromExpr`. This is most of the changes in
`semexprs`.
2. For deref expressions to work in `typeof`, a new type flag
`tfNonConstExpr` is added for `tyFromExpr` that calls `semExprWithType`
with `efInTypeof` on the expression instead of `semConstExpr`. This type
flag is set for every `tyFromExpr` type of a node that `prepareNode`
encounters, so that the node itself isn't evaluated at compile time when
just trying to get the type of the node.
3. Unresolved `static` types matching `static` parameters is now treated
the same as unresolved generic types matching `typedesc` parameters in
generic type bodies, it causes a failed match which delays the call
instantiation.
4. `typedesc` parameters now reject all types containing unresolved
generic types like `seq[T]`, not just generic param types by themselves.
(using `containsGenericType`)
5. `semgnrc` now doesn't leave generic param symbols it encounters in
generic type contexts as just identifiers, and instead turns them into
symbol nodes. Normally in generic procs, this isn't a problem since the
generic param symbols will be provided again at instantiation time (and
in fact creating symbol nodes causes issues since `seminst` doesn't
actually instantiate proc body node types).
But generic types can try to be instantiated early in `sigmatch` which
will give an undeclared identifier error when the param is not provided.
Nodes in generic types (specifically in `tyFromExpr` which should be the
only use for `semGenericStmt`) undergo full generic type instantiation
with `prepareNode`, so there is no issue of these symbols remaining as
uninstantiated generic types.
6. `prepareNode` now has more logic for which nodes to avoid
instantiating.
Subscripts and subscripts turned into calls to `[]` by `semgnrc` need to
avoid instantiating the first operand, since it may be a generic body
type like `Generic` in an expression like `Generic[int]`.
Dot expressions cannot instantiate their RHS as it may be a generic proc
symbol or even an undeclared identifier for generic param fields, but
have to instantiate their LHS, so calls and subscripts need to still
instantiate their first node if it's a dot expression.
This logic still isn't perfect and needs the same level of detail as in
`semexprs` for which nodes can be left as "untyped" for overloading/dot
exprs/subscripts to handle, but should handle the majority of cases.

Also the `efDetermineType` requirement for which calls become
`tyFromExpr` is removed and as a result `efDetermineType` is entirely
unused again.
narimiran pushed a commit that referenced this issue Sep 16, 2024
fixes #23406, closes #23854, closes #23855 (test code of both compiles
but separate issue exists), refs #23432, follows #23411

In generic bodies, previously all regular `nkCall` nodes like `foo(a,
b)` were directly treated as generic statements and delayed immediately,
but other call kinds like `a.foo(b)`, `foo a, b` etc underwent
typechecking before making sure they have to be delayed, as implemented
in #22029. Since the behavior for `nkCall` was slightly buggy (as in

However the vast majority of calls in generic bodies out there are
`nkCall`, and while there isn't a difference in the expected behavior,
this exposes many issues with the implementation started in #22029 given
how much more code uses it now. The portion of these issues that CI has
caught are fixed in this PR but it's possible there are more.

1. Deref expressions, dot expressions and calls to dot expressions now
handle and propagate `tyFromExpr`. This is most of the changes in
`semexprs`.
2. For deref expressions to work in `typeof`, a new type flag
`tfNonConstExpr` is added for `tyFromExpr` that calls `semExprWithType`
with `efInTypeof` on the expression instead of `semConstExpr`. This type
flag is set for every `tyFromExpr` type of a node that `prepareNode`
encounters, so that the node itself isn't evaluated at compile time when
just trying to get the type of the node.
3. Unresolved `static` types matching `static` parameters is now treated
the same as unresolved generic types matching `typedesc` parameters in
generic type bodies, it causes a failed match which delays the call
instantiation.
4. `typedesc` parameters now reject all types containing unresolved
generic types like `seq[T]`, not just generic param types by themselves.
(using `containsGenericType`)
5. `semgnrc` now doesn't leave generic param symbols it encounters in
generic type contexts as just identifiers, and instead turns them into
symbol nodes. Normally in generic procs, this isn't a problem since the
generic param symbols will be provided again at instantiation time (and
in fact creating symbol nodes causes issues since `seminst` doesn't
actually instantiate proc body node types).
But generic types can try to be instantiated early in `sigmatch` which
will give an undeclared identifier error when the param is not provided.
Nodes in generic types (specifically in `tyFromExpr` which should be the
only use for `semGenericStmt`) undergo full generic type instantiation
with `prepareNode`, so there is no issue of these symbols remaining as
uninstantiated generic types.
6. `prepareNode` now has more logic for which nodes to avoid
instantiating.
Subscripts and subscripts turned into calls to `[]` by `semgnrc` need to
avoid instantiating the first operand, since it may be a generic body
type like `Generic` in an expression like `Generic[int]`.
Dot expressions cannot instantiate their RHS as it may be a generic proc
symbol or even an undeclared identifier for generic param fields, but
have to instantiate their LHS, so calls and subscripts need to still
instantiate their first node if it's a dot expression.
This logic still isn't perfect and needs the same level of detail as in
`semexprs` for which nodes can be left as "untyped" for overloading/dot
exprs/subscripts to handle, but should handle the majority of cases.

Also the `efDetermineType` requirement for which calls become
`tyFromExpr` is removed and as a result `efDetermineType` is entirely
unused again.

(cherry picked from commit ab18962)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants