Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,25 @@ proc resetSemFlag(n: PNode) =
for i in 0..<n.safeLen:
resetSemFlag(n[i])

proc normalizeTypedescMacroResult(c: PContext, n: PNode): PNode =
result = n
if result.kind == nkStmtList:
result.transitionSonsKind(nkStmtListType)

# Resolve surviving compile-time branches so later passes don't walk
# unevaluated type AST for a typedesc expression.
while true:
if result.kind == nkWhenStmt:
result = semWhen(c, result, false)
if result.kind == nkStmtList:
result.transitionSonsKind(nkStmtListType)
elif result.kind == nkStmtListType and result.len > 0 and result[^1].kind == nkWhenStmt:
result[^1] = semWhen(c, result[^1], false)
Comment on lines +465 to +470
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The while true loop can become infinite when semWhen(c, ..., false) returns the original nkWhenStmt unchanged (e.g. for when nimvm, where semWhen intentionally returns the when node for codegen, or when cannotResolve in generic contexts leads to semGenericStmt returning an nkWhenStmt). Add a termination guard (e.g. compare with the pre-call node and/or break when the result is still an nkWhenStmt after semWhen) so normalization only repeats when the when was actually reduced to a branch AST.

Suggested change
if result.kind == nkWhenStmt:
result = semWhen(c, result, false)
if result.kind == nkStmtList:
result.transitionSonsKind(nkStmtListType)
elif result.kind == nkStmtListType and result.len > 0 and result[^1].kind == nkWhenStmt:
result[^1] = semWhen(c, result[^1], false)
if result.kind == nkWhenStmt:
let prev = result
result = semWhen(c, result, false)
if result == prev or result.kind == nkWhenStmt:
break
if result.kind == nkStmtList:
result.transitionSonsKind(nkStmtListType)
elif result.kind == nkStmtListType and result.len > 0 and result[^1].kind == nkWhenStmt:
let prev = result[^1]
result[^1] = semWhen(c, result[^1], false)
if result[^1] == prev or result[^1].kind == nkWhenStmt:
break

Copilot uses AI. Check for mistakes.
if result[^1].kind == nkStmtList:
result[^1].transitionSonsKind(nkStmtListType)
else:
break

proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
s: PSym, flags: TExprFlags; expectedType: PType = nil): PNode =
## Semantically check the output of a macro.
Expand Down Expand Up @@ -484,15 +503,15 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
# More restrictive version.
result = semExprWithType(c, result, flags, expectedType)
of tyTypeDesc:
if result.kind == nkStmtList: result.transitionSonsKind(nkStmtListType)
result = normalizeTypedescMacroResult(c, result)
var typ = semTypeNode(c, result, nil)
Comment thread
ringabout marked this conversation as resolved.
if typ == nil:
localError(c.config, result.info, "expression has no type: " &
renderTree(result, {renderNoComments}))
result = newSymNode(errorSym(c, result))
else:
result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
#result = symNodeFromType(c, typ, n.info)
else:
if s.ast[genericParamsPos] != nil and retType.isMetaType:
# The return type may depend on the Macro arguments
Expand Down
Loading