https://github.com/trungnt2910 updated 
https://github.com/llvm/llvm-project/pull/185282

>From f261399901d7c0141f4aac7029f92848ff22ed81 Mon Sep 17 00:00:00 2001
From: Trung Nguyen <[email protected]>
Date: Mon, 9 Mar 2026 01:28:03 +1100
Subject: [PATCH] [clang] Add support for MSVC force inline attrs

Add support for `[[msvc::forceinline]]` and
`[[msvc::forceinline_calls]]`.

`[[msvc::forceinline]]` is equivalent to Microsoft's `__forceinline`
when placed before a function declaration.
Unlike `__forceinline`, `[[msvc::forceinline]]` works with lambdas.

`[[msvc::forceinline_calls]]` is simliar to `[[clang::always_inline]]`
but only works on statements.

Both are implemented as aliases of `[[clang::always_inline]]` with
special checks.

Fixes #186539.
---
 clang/docs/ReleaseNotes.rst                   |  5 +++
 clang/include/clang/Basic/Attr.td             | 10 ++++-
 .../clang/Basic/DiagnosticSemaKinds.td        |  5 +++
 clang/lib/Sema/SemaDeclAttr.cpp               | 12 ++++++
 clang/lib/Sema/SemaStmtAttr.cpp               | 12 +++++-
 clang/test/CodeGen/attr-ms-forceinline.cpp    | 33 ++++++++++++++++
 clang/test/Sema/attr-ms-forceinline.c         | 32 ++++++++++++++++
 clang/test/Sema/attr-ms-forceinline.cpp       | 38 +++++++++++++++++++
 8 files changed, 144 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGen/attr-ms-forceinline.cpp
 create mode 100644 clang/test/Sema/attr-ms-forceinline.c
 create mode 100644 clang/test/Sema/attr-ms-forceinline.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3341ec17c87cd..7f6049b77fa7e 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -252,6 +252,11 @@ Attribute Changes in Clang
   sound because any writer must hold all capabilities, so holding any one
   prevents concurrent writes.
 
+- Added support for ``[[msvc::forceinline]]`` for functions and
+  ``[[msvc::forceinline_calls]]`` for statements. Both are aliases to
+  ``[[clang::always_inline]]`` with additional checks to ensure that they
+  are only accepted in places where MSVC also does.
+
 Improvements to Clang's diagnostics
 -----------------------------------
 - ``-Wunused-but-set-variable`` now diagnoses file-scope variables with
diff --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 8d716bd05b6ab..328e70b3ed900 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -934,9 +934,15 @@ def AlignNatural : InheritableAttr {
 
 def AlwaysInline : DeclOrStmtAttr {
   let Spellings = [GCC<"always_inline">, CXX11<"clang", "always_inline">,
-                   C23<"clang", "always_inline">, 
CustomKeyword<"__forceinline">];
+                   C23<"clang", "always_inline">, 
CustomKeyword<"__forceinline">,
+                   CXX11<"msvc", "forceinline">, C23<"msvc", "forceinline">,
+                   CXX11<"msvc", "forceinline_calls">, C23<"msvc", 
"forceinline_calls">];
   let Accessors = [Accessor<"isClangAlwaysInline", [CXX11<"clang", 
"always_inline">,
-                                                    C23<"clang", 
"always_inline">]>];
+                                                    C23<"clang", 
"always_inline">]>,
+                   Accessor<"isMSVCForceInline", [CXX11<"msvc", "forceinline">,
+                                                  C23<"msvc", "forceinline">]>,
+                   Accessor<"isMSVCForceInlineCalls", [CXX11<"msvc", 
"forceinline_calls">,
+                                                       C23<"msvc", 
"forceinline_calls">]>];
   let Subjects = SubjectList<[Function, Stmt], WarnDiag,
                              "functions and statements">;
   let Documentation = [AlwaysInlineDocs];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6b9fa4a257397..603e91d598f79 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3277,6 +3277,11 @@ def warn_function_attribute_ignored_in_stmt : Warning<
   "use '%0' on statements">,
   InGroup<IgnoredAttributes>;
 
+def warn_stmt_attribute_ignored_in_function : Warning<
+  "attribute is ignored on this function as it only applies to statements; "
+  "use '%0' for functions">,
+  InGroup<IgnoredAttributes>;
+
 def err_musttail_needs_trivial_args : Error<
   "tail call requires that the return value, all parameters, and any "
   "temporaries created by the expression are trivially destructible">;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 63a70e4b77bc4..8a856215a9627 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5181,6 +5181,18 @@ OptimizeNoneAttr *Sema::mergeOptimizeNoneAttr(Decl *D,
 }
 
 static void handleAlwaysInlineAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  AlwaysInlineAttr AIA(S.Context, AL);
+  if (!S.getLangOpts().MicrosoftExt &&
+      (AIA.isMSVCForceInline() || AIA.isMSVCForceInlineCalls())) {
+    S.Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL;
+    return;
+  }
+  if (AIA.isMSVCForceInlineCalls()) {
+    S.Diag(AL.getLoc(), diag::warn_stmt_attribute_ignored_in_function)
+        << "[[msvc::forceinline]]";
+    return;
+  }
+
   if (AlwaysInlineAttr *Inline =
           S.mergeAlwaysInlineAttr(D, AL, AL.getAttrName()))
     D->addAttr(Inline);
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index f7de98a3e5cf0..58d5332565d10 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -314,7 +314,17 @@ static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const 
ParsedAttr &A,
 static Attr *handleAlwaysInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
                                     SourceRange Range) {
   AlwaysInlineAttr AIA(S.Context, A);
-  if (!AIA.isClangAlwaysInline()) {
+  if (!S.getLangOpts().MicrosoftExt &&
+      (AIA.isMSVCForceInline() || AIA.isMSVCForceInlineCalls())) {
+    S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored) << A;
+    return nullptr;
+  }
+  if (AIA.isMSVCForceInline()) {
+    S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
+        << "[[msvc::forceinline_calls]]";
+    return nullptr;
+  }
+  if (!AIA.isClangAlwaysInline() && !AIA.isMSVCForceInlineCalls()) {
     S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
         << "[[clang::always_inline]]";
     return nullptr;
diff --git a/clang/test/CodeGen/attr-ms-forceinline.cpp 
b/clang/test/CodeGen/attr-ms-forceinline.cpp
new file mode 100644
index 0000000000000..795e6705f7826
--- /dev/null
+++ b/clang/test/CodeGen/attr-ms-forceinline.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -std=c++23 -disable-llvm-passes -emit-llvm %s -triple 
x86_64-pc-windows-msvc -fms-extensions -o - | FileCheck 
-check-prefix=CHECK-MSVC %s
+// RUN: %clang_cc1 -std=c++23 -disable-llvm-passes -emit-llvm %s -triple 
x86_64-unknown-linux-gnu -fms-extensions -o - | FileCheck 
-check-prefix=CHECK-LINUX %s
+
+// CHECK-MSVC: define {{.*}} @"?foo@@YAXXZ"() #[[ATTR_FOO:[0-9]+]]
+// CHECK-LINUX: define {{.*}} @_Z3foov() #[[ATTR_FOO:[0-9]+]]
+// Add another attribute to prevent overlapping sets.
+[[msvc::forceinline, gnu::hot]] void foo() {}
+
+void bar();
+
+void call_lambda() {
+  auto lambda = [] [[msvc::forceinline]] () { bar(); };
+  lambda();
+}
+
+// CHECK-MSVC: define internal void 
@"??R<lambda_0>@?0??call_lambda@@YAXXZ@QEBA?A?<auto>@@XZ"{{.*}} 
#[[ATTR_LAMBDA:[0-9]+]]
+// CHECK-LINUX: define internal void @"_ZZ11call_lambdavENK3$_0clEv"{{.*}} 
#[[ATTR_LAMBDA:[0-9]+]]
+
+void call_bar() {
+// CHECK-MSVC-LABEL: define {{.*}} @"?call_bar@@YAXXZ"()
+// CHECK-MSVC: call void @"?bar@@YAXXZ"() #[[ATTR_CALLSITE:[0-9]+]]
+// CHECK-LINUX-LABEL: define {{.*}} @_Z8call_barv()
+// CHECK-LINUX: call void @_Z3barv() #[[ATTR_CALLSITE:[0-9]+]]
+  [[msvc::forceinline_calls]] bar();
+}
+
+// CHECK-MSVC-DAG: attributes #[[ATTR_FOO]] = { alwaysinline hot {{.*}}}
+// CHECK-MSVC-DAG: attributes #[[ATTR_LAMBDA]] = { alwaysinline {{.*}}}
+// CHECK-MSVC-DAG: attributes #[[ATTR_CALLSITE]] = { alwaysinline }
+
+// CHECK-LINUX-DAG: attributes #[[ATTR_FOO]] = { alwaysinline hot {{.*}}}
+// CHECK-LINUX-DAG: attributes #[[ATTR_LAMBDA]] = { alwaysinline {{.*}}}
+// CHECK-LINUX-DAG: attributes #[[ATTR_CALLSITE]] = { alwaysinline }
diff --git a/clang/test/Sema/attr-ms-forceinline.c 
b/clang/test/Sema/attr-ms-forceinline.c
new file mode 100644
index 0000000000000..1da92e89f2178
--- /dev/null
+++ b/clang/test/Sema/attr-ms-forceinline.c
@@ -0,0 +1,32 @@
+// RUN: %clang_cc1 -verify=ms-ext -fms-extensions -fsyntax-only -std=c23 %s
+// RUN: %clang_cc1 -verify=no-ms-ext -fsyntax-only -std=c23 %s
+
+void foo(void);
+
+[[msvc::forceinline]] void func(void) {}
+// no-ms-ext-warning@-1 {{'msvc::forceinline' attribute ignored}}
+
+void stmt_forceinline(void) {
+  [[msvc::forceinline]] func();
+  // ms-ext-warning@-1 {{attribute is ignored on this statement as it only 
applies to functions; use '[[msvc::forceinline_calls]]' on statements}}
+  // no-ms-ext-warning@-2 {{'msvc::forceinline' attribute ignored}}
+}
+
+[[msvc::forceinline_calls]] void func2(void) {}
+// ms-ext-warning@-1 {{attribute is ignored on this function as it only 
applies to statements; use '[[msvc::forceinline]]' for functions}}
+// no-ms-ext-warning@-2 {{'msvc::forceinline_calls' attribute ignored}}
+
+void stmt_forceinline_calls(void) {
+  [[msvc::forceinline_calls]] foo();
+  // no-ms-ext-warning@-1 {{'msvc::forceinline_calls' attribute ignored}}
+}
+
+[[msvc::forceinline(0)]] void func3(void);
+// ms-ext-error@-1 {{'msvc::forceinline' attribute takes no arguments}}
+// no-ms-ext-error@-2 {{'msvc::forceinline' attribute takes no arguments}}
+
+void func4(void) {
+  [[msvc::forceinline_calls("foo")]] foo();
+  // ms-ext-error@-1 {{'msvc::forceinline_calls' attribute takes no arguments}}
+  // no-ms-ext-error@-2 {{'msvc::forceinline_calls' attribute takes no 
arguments}}
+}
diff --git a/clang/test/Sema/attr-ms-forceinline.cpp 
b/clang/test/Sema/attr-ms-forceinline.cpp
new file mode 100644
index 0000000000000..5e1d2b3eeee85
--- /dev/null
+++ b/clang/test/Sema/attr-ms-forceinline.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -verify=ms-ext -fms-extensions -fsyntax-only -std=c++23 %s
+// RUN: %clang_cc1 -verify=no-ms-ext -fsyntax-only -std=c++23 %s
+
+void foo();
+
+[[msvc::forceinline]] void func() {}
+// no-ms-ext-warning@-1 {{'msvc::forceinline' attribute ignored}}
+
+void lambda_func() {
+  auto l = [] [[msvc::forceinline]] () {};
+  // no-ms-ext-warning@-1 {{'msvc::forceinline' attribute ignored}}
+  l();
+}
+
+void stmt_forceinline() {
+  [[msvc::forceinline]] func();
+  // ms-ext-warning@-1 {{attribute is ignored on this statement as it only 
applies to functions; use '[[msvc::forceinline_calls]]' on statements}}
+  // no-ms-ext-warning@-2 {{'msvc::forceinline' attribute ignored}}
+}
+
+[[msvc::forceinline_calls]] void func2() {}
+// ms-ext-warning@-1 {{attribute is ignored on this function as it only 
applies to statements; use '[[msvc::forceinline]]' for functions}}
+// no-ms-ext-warning@-2 {{'msvc::forceinline_calls' attribute ignored}}
+
+void stmt_forceinline_calls() {
+  [[msvc::forceinline_calls]] foo();
+  // no-ms-ext-warning@-1 {{'msvc::forceinline_calls' attribute ignored}}
+}
+
+[[msvc::forceinline(0)]] void func3();
+// ms-ext-error@-1 {{'msvc::forceinline' attribute takes no arguments}}
+// no-ms-ext-error@-2 {{'msvc::forceinline' attribute takes no arguments}}
+
+void func4() {
+  [[msvc::forceinline_calls("foo")]] foo();
+  // ms-ext-error@-1 {{'msvc::forceinline_calls' attribute takes no arguments}}
+  // no-ms-ext-error@-2 {{'msvc::forceinline_calls' attribute takes no 
arguments}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to