From 8cb406cd7a37013ea84be3fec728f0db78a900a9 Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Sat, 29 Aug 2026 08:41:05 -0400 Subject: [PATCH] Fix 26144; exception propagation for non-raising virtual methods (#26145) ref #26144 The C backend must not use `sfNeverRaises` to remove exception checks from virtual method calls. The flag describes only the selected base method body, while a vtable override may raise a catchable exception. This change makes `canRaiseDisp` conservative for `skMethod` symbols and adds a regression test covering an exception raised by a child method invoked through a base reference. --- compiler/ccgcalls.nim | 6 +++++- tests/method/tmethod_virtual_raise.nim | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/method/tmethod_virtual_raise.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 1202aa0536..77c09d82cf 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -11,7 +11,11 @@ proc canRaiseDisp(p: BProc; n: PNode): bool = # we assume things like sysFatal cannot raise themselves - if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: + if n.kind == nkSym and n.sym.kind == skMethod: + # A base method may be overridden by a branch with a wider exception set. + # Its inferred effects describe only the base body, not every vtable target. + result = true + elif n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}: result = false elif optPanics in p.config.globalOptions or (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and diff --git a/tests/method/tmethod_virtual_raise.nim b/tests/method/tmethod_virtual_raise.nim new file mode 100644 index 0000000000..6bc1575282 --- /dev/null +++ b/tests/method/tmethod_virtual_raise.nim @@ -0,0 +1,20 @@ +discard """ + output: '''caught''' +""" + +type + Base = ref object of RootObj + Child = ref object of Base + +method run(value: Base): string {.base.} = + result = "base" + +method run(value: Child): string = + raise newException(ValueError, "child") + +let value: Base = Child() +try: + discard value.run() + quit "virtual method did not raise" +except ValueError: + echo "caught"