fix #25976: treat proc-type forbids as an empty tag set (#25980)

fix #25976

Initialize tagEffects for proc types that declare .forbids but omit
.tags,
so they behave like explicit tags: [] during indirect-call effect
tracking.
Add a regression for the nested callback assignment case.
This commit is contained in:
ringabout
2026-07-10 13:39:32 +08:00
committed by GitHub
parent 1dc079b723
commit 4b1444e728
2 changed files with 34 additions and 1 deletions

View File

@@ -1784,13 +1784,18 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) =
elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
effects[exceptionEffects] = newNodeI(nkArgList, effects.info)
let forbidsSpec = effectSpec(n, wForbids)
let tagsSpec = effectSpec(n, wTags)
if not isNil(tagsSpec):
effects[tagEffects] = tagsSpec
elif not isNil(forbidsSpec):
# `.forbids` without `.tags` still declares a known empty tag set.
# Leaving this as nil would mean "unknown tags", which later widens
# indirect calls to `RootEffect`.
effects[tagEffects] = newNodeI(nkArgList, effects.info)
elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):
effects[tagEffects] = newNodeI(nkArgList, effects.info)
let forbidsSpec = effectSpec(n, wForbids)
if not isNil(forbidsSpec):
effects[forbiddenEffects] = forbidsSpec
elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}):

View File

@@ -0,0 +1,28 @@
type
NestedPoll = object of RootEffect
CallbackFunc = proc(arg: pointer) {.gcsafe, raises: [], forbids: [NestedPoll].}
TaggedCallbackFunc = proc(arg: pointer) {.gcsafe, raises: [], tags: [], forbids: [NestedPoll].}
InternalAsyncCallback = object
fn: CallbackFunc
TaggedInternalAsyncCallback = object
fn: TaggedCallbackFunc
proc closeSocket(aftercb: CallbackFunc = nil) =
proc continuation(udata: pointer) =
aftercb(nil)
let acb = InternalAsyncCallback(fn: continuation)
discard acb
proc closeSocketTagged(aftercb: TaggedCallbackFunc = nil) =
proc continuation(udata: pointer) =
aftercb(nil)
let acb = TaggedInternalAsyncCallback(fn: continuation)
discard acb
closeSocket()
closeSocketTagged()