The two auto-inline tiers treat annotations asymmetrically (src/ast/ast_inline.cpp):
- Functions are conservative:
isAnnotated (~line 590) refuses best-effort inlining for any annotation other than export / unused_argument, on the stated ground that annotations may carry call-site semantics (verifyCall, transform, per-call codegen) that splicing the call away would bypass.
- Block literals are permissive:
canBlockInline (~line 1631) checks only for [never_inline] among the block's annotations. Any other block annotation does not decline the splice, and the InvokeBlock arm then executes bodyClone->annotations.clear() (~line 2582) — the annotation and whatever semantics it carried vanish silently.
So a block literal carrying a semantic block annotation (a macro-attached AstBlockAnnotation, or any future block-level marker) is inlined with the annotation discarded, with no diagnostic. The function tier's own comment states exactly why this is unsound.
Expected: the block tier should mirror the function tier — refuse to inline a block whose annotation list contains anything beyond known-inert markers, or at minimum refuse instead of clearing.
Found while working around a related constraint in the same pass (a [never_inline] marker was needed on MSL helper functions so the auto-inliner would not fold them back into a template method whose value-position splice requires a single-return body).
The two auto-inline tiers treat annotations asymmetrically (
src/ast/ast_inline.cpp):isAnnotated(~line 590) refuses best-effort inlining for any annotation other thanexport/unused_argument, on the stated ground that annotations may carry call-site semantics (verifyCall, transform, per-call codegen) that splicing the call away would bypass.canBlockInline(~line 1631) checks only for[never_inline]among the block's annotations. Any other block annotation does not decline the splice, and theInvokeBlockarm then executesbodyClone->annotations.clear()(~line 2582) — the annotation and whatever semantics it carried vanish silently.So a block literal carrying a semantic block annotation (a macro-attached
AstBlockAnnotation, or any future block-level marker) is inlined with the annotation discarded, with no diagnostic. The function tier's own comment states exactly why this is unsound.Expected: the block tier should mirror the function tier — refuse to inline a block whose annotation list contains anything beyond known-inert markers, or at minimum refuse instead of clearing.
Found while working around a related constraint in the same pass (a
[never_inline]marker was needed on MSL helper functions so the auto-inliner would not fold them back into a template method whose value-position splice requires a single-returnbody).