From cfa769fefc86c2f51f5647605622e19a349dd7ee Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 28 May 2026 05:28:27 +0800 Subject: [PATCH] fixes #22950; Poor error message on cast effect violation (#25839) fixes #22950 This pull request improves the tracking and reporting of effect annotations (such as `raises`, `tags`, and `forbids`) in pragma blocks, particularly when using the `cast` pragma. It ensures that the source of these effect annotations is correctly preserved and referenced, which improves error reporting and effect analysis. Additionally, a new test was added to check for violations when using `cast` with effect annotations. Effect annotation source tracking and propagation: * Added new fields (`excSource`, `tagsSource`, `forbidsSource`) to the `PragmaBlockContext` type to store the original source node for each effect annotation. * Updated `castBlock` to set these new source fields when processing `raises`, `tags`, and `forbids` pragmas, ensuring the source node is preserved for later error reporting. * Modified `unapplyBlockContext` to use the stored source node (if available) when calling `addRaiseEffect`, `addTag`, and `addNotTag`, improving the accuracy of effect tracking and diagnostics. Pragma handling improvements: * Changed the call to `castBlock` in the main pragma processing loop to pass the entire pragma node, enabling access to the original source for effect annotations. Testing: * Added a new test (`tests/effects/tcast_effect_violation.nim`) to verify that using `cast(raises: ValueError)` inside a procedure with `.raises: [].` correctly triggers an error message about an unlisted exception. --- compiler/sempass2.nim | 15 ++++++++++----- tests/effects/tcast_effect_violation.nim | 8 ++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 tests/effects/tcast_effect_violation.nim diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 9c84b721ad..f1e2e69cbe 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1208,6 +1208,7 @@ type enforcedGcSafety, enforceNoSideEffects: bool oldExc, oldTags, oldForbids: int exc, tags, forbids: PNode + excSource, tagsSource, forbidsSource: PNode proc createBlockContext(tracked: PEffects): PragmaBlockContext = var oldForbidsLen = 0 @@ -1230,17 +1231,18 @@ proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) = # anything about 'raises' in the 'cast' at all. Same applies for 'tags'. setLen(tracked.exc.sons, bc.oldExc) for e in bc.exc: - addRaiseEffect(tracked, e, e) + addRaiseEffect(tracked, e, if bc.excSource != nil: bc.excSource else: e) if bc.tags != nil: setLen(tracked.tags.sons, bc.oldTags) for t in bc.tags: - addTag(tracked, t, t) + addTag(tracked, t, if bc.tagsSource != nil: bc.tagsSource else: t) if bc.forbids != nil: setLen(tracked.forbids.sons, bc.oldForbids) for t in bc.forbids: - addNotTag(tracked, t, t) + addNotTag(tracked, t, if bc.forbidsSource != nil: bc.forbidsSource else: t) -proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = +proc castBlock(tracked: PEffects, castPragma: PNode, bc: var PragmaBlockContext) = + let pragma = castPragma[1] case whichPragma(pragma) of wGcSafe: bc.enforcedGcSafety = true @@ -1253,6 +1255,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = else: bc.tags = newNodeI(nkArgList, pragma.info) bc.tags.add n + bc.tagsSource = castPragma of wForbids: let n = pragma[1] if n.kind in {nkCurly, nkBracket}: @@ -1260,6 +1263,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = else: bc.forbids = newNodeI(nkArgList, pragma.info) bc.forbids.add n + bc.forbidsSource = castPragma of wRaises: let n = pragma[1] if n.kind in {nkCurly, nkBracket}: @@ -1267,6 +1271,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = else: bc.exc = newNodeI(nkArgList, pragma.info) bc.exc.add n + bc.excSource = castPragma of wUncheckedAssign: discard "handled in sempass1" else: @@ -1520,7 +1525,7 @@ proc track(tracked: PEffects, n: PNode) = of wNoSideEffect: bc.enforceNoSideEffects = true of wCast: - castBlock(tracked, pragmaList[i][1], bc) + castBlock(tracked, pragmaList[i], bc) else: discard applyBlockContext(tracked, bc) diff --git a/tests/effects/tcast_effect_violation.nim b/tests/effects/tcast_effect_violation.nim new file mode 100644 index 0000000000..fe7f93cc4a --- /dev/null +++ b/tests/effects/tcast_effect_violation.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "cast(raises: ValueError) can raise an unlisted exception: ValueError" + line: 7 +""" + +proc fff() {.raises: [].} = + {.cast(raises: ValueError).}: + discard \ No newline at end of file