From 3d44d59f7c080f2b5d0076724dad8474bbbc7130 Mon Sep 17 00:00:00 2001 From: Francesco Pretto Date: Sat, 12 Sep 2026 09:49:46 +0200 Subject: [PATCH] State that [[msvc::forceinline]] behaves similarly to __forceinline but preserves the symbol in the TU The documentation treated [[msvc::forceinline]] as a synonym of __forceinline, but they don't have the same exact behavior. In a empirical test, a Link Time Optimization enabled build of a static library had a method marked as __forceinline elided from the final objects, causing a linking error when trying to consume the static library from a non-LTO enabled exe or shared library. The method definition is *not* present/visible in the header. Instead, [[msvc::forceinline]] was able to both intra-optimize the static library and create a symbol for the annotated function, allowing link from a consumer which has not visibility of the definition. For more context, the behavior of [[msvc::forceinline]] aligns with the behavior of the gcc specific always_inline[1] attribute, and clang also observes the same behavior. Below the results of a probe using VS 2026 and VS 2022. ----------------------------------------------- == toolchains == -- 18 (18.10.12201.205): Visual Studio 18 2026 C:\Program Files\Microsoft Visual Studio\18\Community -- 2022 (17.14.37531.7): Visual Studio 17 2022 C:\Program Files\Microsoft Visual Studio\2022\Community -- cmake: cmake version 4.3.2 == results == VS Annotation LTO Library Exe Shlib Symbols Note ---- --------------------- --- ------- -------------- -------------- -------- ---- 18 none OFF OK LINK-OK RUN-OK LINK-OK RUN-OK present 18 none ON OK LINK-OK RUN-OK LINK-OK RUN-OK n/a (IL) 18 __forceinline OFF OK LINK-FAIL LINK-FAIL absent 18 __forceinline ON OK LINK-FAIL LINK-FAIL n/a (IL) 18 [[msvc::forceinline]] OFF OK LINK-OK RUN-OK LINK-OK RUN-OK present 18 [[msvc::forceinline]] ON OK LINK-OK RUN-OK LINK-OK RUN-OK n/a (IL) 2022 none OFF OK LINK-OK RUN-OK LINK-OK RUN-OK present 2022 none ON OK LINK-OK RUN-OK LINK-OK RUN-OK n/a (IL) 2022 __forceinline OFF OK LINK-FAIL LINK-FAIL absent 2022 __forceinline ON OK LINK-FAIL LINK-FAIL n/a (IL) 2022 [[msvc::forceinline]] OFF OK LINK-OK RUN-OK LINK-OK RUN-OK present 2022 [[msvc::forceinline]] ON OK LINK-OK RUN-OK LINK-OK RUN-OK n/a (IL) == verdict == 18 lto=OFF: PARITY, [[msvc::forceinline]] keeps the symbol where __forceinline drops it 18 lto=ON: PARITY, [[msvc::forceinline]] keeps the symbol where __forceinline drops it 2022 lto=OFF: PARITY, [[msvc::forceinline]] keeps the symbol where __forceinline drops it 2022 lto=ON: PARITY, [[msvc::forceinline]] keeps the symbol where __forceinline drops it ----------------------------------------------- [1] https://gcc.gnu.org/onlinedocs/gcc/Common-Attributes.html#index-always_005finline --- docs/cpp/attributes.md | 2 +- docs/cpp/inline-functions-cpp.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cpp/attributes.md b/docs/cpp/attributes.md index 5e6cfc32fc..20f89c6ed1 100644 --- a/docs/cpp/attributes.md +++ b/docs/cpp/attributes.md @@ -177,7 +177,7 @@ The Microsoft-specific attribute `[[msvc::flatten]]` is similar to `[[msvc::forc ### `[[msvc::forceinline]]` -When placed before a function declaration, the Microsoft-specific attribute `[[msvc::forceinline]]` has the same meaning as `__forceinline`. +When placed before a function declaration, the Microsoft-specific attribute `[[msvc::forceinline]]` behaves similarly to `__forceinline`, but preserves the creation of a symbol in the translation unit where the function is defined. ### `[[msvc::forceinline_calls]]` diff --git a/docs/cpp/inline-functions-cpp.md b/docs/cpp/inline-functions-cpp.md index f67599ff01..3d5bde786e 100644 --- a/docs/cpp/inline-functions-cpp.md +++ b/docs/cpp/inline-functions-cpp.md @@ -180,7 +180,7 @@ The C++ Standard defines a common set of attributes. It also allows compiler ven |Attribute | Meaning | |---------|---------| -| [`[msvc::forceinline]`](/cpp/cpp/attributes#msvcforceinline)| Has the same meaning as **`__forceinline`**.| +| [`[msvc::forceinline]`](/cpp/cpp/attributes#msvcforceinline)| It behaves similarly to **`__forceinline`**, but preserves the creation of a symbol in the translation unit where the function (to which it is applied) is defined.| | [`[msvc::forceinline_calls]`](/cpp/cpp/attributes#msvcforceinline_calls) | Can be placed on or before a statement or block to cause the inline heuristic to force-inline all calls in that statement or block.| | [`[msvc::flatten]`](/cpp/cpp/attributes#msvcflatten) | Similar to `[[msvc::forceinline_calls]]`, but recursively force-inlines all calls in the scope it's applied to until no calls are left. | | [`[msvc::noinline]`](/cpp/cpp/attributes#msvcnoinline) | When placed before a function declaration, has the same meaning as `__declspec(noinline)`. |