[clang] Rewrite offsetof with __builtin_offsetof with -fms-kernel (PR #193804)
via cfe-commits
cfe-commits at lists.llvm.org
Tue May 5 10:29:45 PDT 2026
https://github.com/eleviant updated https://github.com/llvm/llvm-project/pull/193804
>From 10961176c65a0ee7bc86c5ed70683f9e6a470534 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Fri, 10 Apr 2026 20:13:58 +0200
Subject: [PATCH 1/4] Rewrite offsetof with __builtin_offsetof with -fms-kernel
Patch rewrites portion of AST if it is detected as offset calculation,
e.g (size_t)&(((MyStruct*)0)->field). This expression is being rewritten
by this patch as (size_t)__builtin_offsetof(MyStruct, field). Patch may
affect compiler warnings, see test cases for details.
---
clang/include/clang/Sema/Sema.h | 4 +
clang/lib/Sema/CMakeLists.txt | 1 +
clang/lib/Sema/SemaCast.cpp | 6 +
clang/lib/Sema/SemaMSKernel.cpp | 71 +++
clang/test/CodeGen/MSKernel/ast-bytecode-c.c | 455 ++++++++++++++++++
.../CodeGen/MSKernel/containing-record.cpp | 57 +++
clang/test/CodeGen/MSKernel/nullptr.cl | 25 +
clang/test/CodeGen/MSKernel/offsetof.cpp | 39 ++
clang/test/CodeGen/MSKernel/struct-decl.c | 114 +++++
9 files changed, 772 insertions(+)
create mode 100644 clang/lib/Sema/SemaMSKernel.cpp
create mode 100644 clang/test/CodeGen/MSKernel/ast-bytecode-c.c
create mode 100644 clang/test/CodeGen/MSKernel/containing-record.cpp
create mode 100644 clang/test/CodeGen/MSKernel/nullptr.cl
create mode 100644 clang/test/CodeGen/MSKernel/offsetof.cpp
create mode 100644 clang/test/CodeGen/MSKernel/struct-decl.c
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 20f7d84cfc475..57262373486f7 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12430,6 +12430,10 @@ class Sema final : public SemaBase {
DeclarationName Name);
bool RebuildNestedNameSpecifierInCurrentInstantiation(CXXScopeSpec &SS);
+ /// Do MS kernel specific AST transformations, e.g replace
+ /// &((type*)0)->member with __builtin_offsetof(type, member)
+ ExprResult TransformForMSKernel(Expr *UOp);
+
ExprResult RebuildExprInCurrentInstantiation(Expr *E);
/// Rebuild the template parameters now that we know we're in a current
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 0ebf56ecffe69..8113e6714a076 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -67,6 +67,7 @@ add_clang_library(clangSema
SemaLoongArch.cpp
SemaM68k.cpp
SemaMIPS.cpp
+ SemaMSKernel.cpp
SemaMSP430.cpp
SemaModule.cpp
SemaNVPTX.cpp
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 0040f3aa1a891..589c978d82248 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -3478,6 +3478,12 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc,
Op.checkQualifiedDestType();
+ if (Op.Kind == CK_PointerToIntegral && Context.getLangOpts().Kernel) {
+ ExprResult Transformed =
+ TransformForMSKernel(Op.SrcExpr.get()->IgnoreParenImpCasts());
+ if (Transformed.isUsable())
+ return BuildCStyleCastExpr(LPLoc, CastTypeInfo, RPLoc, Transformed.get());
+ }
return Op.complete(CStyleCastExpr::Create(
Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(),
&Op.BasePath, CurFPFeatureOverrides(), CastTypeInfo, LPLoc, RPLoc));
diff --git a/clang/lib/Sema/SemaMSKernel.cpp b/clang/lib/Sema/SemaMSKernel.cpp
new file mode 100644
index 0000000000000..6d70ebf4411ac
--- /dev/null
+++ b/clang/lib/Sema/SemaMSKernel.cpp
@@ -0,0 +1,71 @@
+#include "TreeTransform.h"
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaConsumer.h"
+
+using namespace clang;
+
+namespace {
+ExprResult TransformUnaryOperator(Sema &SemaRef, UnaryOperator *UO) {
+ if (UO->getOpcode() != UO_AddrOf)
+ return {};
+
+ SmallVector<Sema::OffsetOfComponent, 4> Components;
+ Expr *Current = UO->getSubExpr()->IgnoreParens();
+ while (true) {
+ // Strip away the "noise" added by array decays or parentheses
+ Current = Current->IgnoreParenImpCasts();
+ Sema::OffsetOfComponent Comp;
+ Comp.LocStart = Current->getBeginLoc();
+ Comp.LocEnd = Current->getEndLoc();
+
+ if (auto *ME = dyn_cast<MemberExpr>(Current)) {
+ Comp.isBrackets = false;
+ Comp.U.IdentInfo = ME->getMemberDecl()->getIdentifier();
+ Components.push_back(Comp);
+ Current = ME->getBase();
+ } else if (auto *ASE = dyn_cast<ArraySubscriptExpr>(Current)) {
+ Comp.isBrackets = true;
+ // In offsetof, the index must be an expression
+ Comp.U.E = ASE->getIdx();
+ Components.push_back(Comp);
+ Current = ASE->getBase();
+ } else {
+ // No more members or subscripts
+ break;
+ }
+ }
+ // Verify we ended at a Null Pointer Cast
+ Expr *Base = Current->IgnoreParenCasts();
+ if (!Components.empty() &&
+ Base->isNullPointerConstant(SemaRef.Context,
+ Expr::NPC_ValueDependentIsNotNull)) {
+ // Don't treat &((MyStruct*)0)[1] as an offsetof expression
+ if (Components.back().isBrackets)
+ return {};
+ // Targets like amdgcn, where nullptr != 0, are ignored
+ if (SemaRef.Context.getTargetNullPointerValue(Current->getType()))
+ return {};
+ std::reverse(Components.begin(), Components.end());
+
+ // Get the root structure type (e.g., MyStruct)
+ TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(
+ Current->getType()->getPointeeType(), Current->getBeginLoc());
+ return SemaRef.BuildBuiltinOffsetOf(UO->getBeginLoc(), TInfo, Components,
+ UO->getEndLoc());
+ }
+
+ return {};
+}
+} // end anonymous namespace
+
+ExprResult clang::Sema::TransformForMSKernel(Expr *E) {
+ auto *UO = dyn_cast_or_null<UnaryOperator>(E);
+ if (!UO)
+ return {};
+ ExprResult NewUO = TransformUnaryOperator(*this, UO);
+ return NewUO.isUsable() ? NewUO : ExprResult();
+}
diff --git a/clang/test/CodeGen/MSKernel/ast-bytecode-c.c b/clang/test/CodeGen/MSKernel/ast-bytecode-c.c
new file mode 100644
index 0000000000000..c83feccd4d486
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/ast-bytecode-c.c
@@ -0,0 +1,455 @@
+// This test is a modified version of AST/ByteCode/c.c
+// Original test fails in case entire LLVM test suite is compiled with -fms-kernel
+// Changes are minor and reflect differences in handling of __builtin_offsetof vs
+// offsetof and long type size in Windows vs Linux (4 vs 8)
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=expected,all -std=c11 -Wcast-qual %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=pedantic,pedantic-expected,all -std=c11 -Wcast-qual -pedantic %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=ref,all -std=c11 -Wcast-qual %s
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=pedantic,pedantic-ref,all -std=c11 -Wcast-qual -pedantic %s
+
+typedef __INTPTR_TYPE__ intptr_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+
+_Static_assert(1, "");
+
+_Static_assert(__objc_yes, "");
+_Static_assert(!__objc_no, "");
+
+_Static_assert(0 != 1, "");
+_Static_assert(1.0 == 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+_Static_assert(1 && 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+_Static_assert( (5 > 4) + (3 > 2) == 2, "");
+_Static_assert(!!1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+_Static_assert(!!1, "");
+
+_Static_assert(!(_Bool){(void*)0}, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+
+int a = (1 == 1 ? 5 : 3);
+_Static_assert(a == 5, ""); // all-error {{not an integral constant expression}}
+
+const int DiscardedPtrToIntCast = ((intptr_t)((void*)0), 0); // all-warning {{left operand of comma operator has no effect}}
+
+const int b = 3;
+_Static_assert(b == 3, ""); // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-expected-warning {{not an integer constant expression}}
+
+const int c; // all-note {{declared here}}
+_Static_assert(c == 0, ""); // ref-error {{not an integral constant expression}} \
+ // ref-note {{initializer of 'c' is unknown}} \
+ // pedantic-ref-error {{not an integral constant expression}} \
+ // pedantic-ref-note {{initializer of 'c' is unknown}} \
+ // expected-error {{not an integral constant expression}} \
+ // expected-note {{initializer of 'c' is unknown}} \
+ // pedantic-expected-error {{not an integral constant expression}} \
+ // pedantic-expected-note {{initializer of 'c' is unknown}}
+
+_Static_assert(&c != 0, ""); // ref-warning {{always true}} \
+ // pedantic-ref-warning {{always true}} \
+ // pedantic-ref-warning {{is a GNU extension}} \
+ // expected-warning {{always true}} \
+ // pedantic-expected-warning {{always true}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert(&a != 0, ""); // ref-warning {{always true}} \
+ // pedantic-ref-warning {{always true}} \
+ // pedantic-ref-warning {{is a GNU extension}} \
+ // expected-warning {{always true}} \
+ // pedantic-expected-warning {{always true}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert((&c + 1) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert((&a + 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
+ // pedantic-ref-note {{100 of non-array}} \
+ // pedantic-expected-note {{100 of non-array}} \
+ // pedantic-expected-warning {{is a GNU extension}}
+_Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
+ // pedantic-expected-warning {{is a GNU extension}} \
+ // pedantic-ref-note {{-100 of non-array}} \
+ // pedantic-expected-note {{-100 of non-array}}
+/// extern variable of a composite type.
+extern struct Test50S Test50;
+_Static_assert(&Test50 != (void*)0, ""); // all-warning {{always true}} \
+ // pedantic-warning {{is a GNU extension}} \
+ // pedantic-note {{this conversion is not allowed in a constant expression}}
+
+struct y {int x,y;};
+int a2[(intptr_t)&((struct y*)0)->y];
+
+const struct y *yy = (struct y*)0;
+const intptr_t L = (intptr_t)(&(yy->y)); // all-error {{not a compile-time constant}}
+
+_Static_assert((long)&((struct y*)0)->y > 0, ""); // all-warning {{cast to smaller integer type 'long' from 'int *'}}
+
+const ptrdiff_t m = &m + 137 - &m;
+_Static_assert(m == 137, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+/// from test/Sema/switch.c, used to cause an assertion failure.
+void f (int z) {
+ while (z) {
+ default: z--; // all-error {{'default' statement not in switch}}
+ }
+}
+
+int expr;
+int chooseexpr[__builtin_choose_expr(1, 1, expr)];
+
+int somefunc(int i) {
+ return (i, 65537) * 65537; // all-warning {{left operand of comma operator has no effect}} \
+ // all-warning {{overflow in expression; result is 131'073 with type 'int'}}
+}
+
+#pragma clang diagnostic ignored "-Wpointer-to-int-cast"
+struct ArrayStruct {
+ char n[1];
+};
+
+// With -fms-kernel we replace offsetof expression with __builtin_offsetof
+// This eliminates original warning (folded to constant array), but
+// introduces another one (zero size array is an extension)
+char name2[(int)&((struct ArrayStruct*)0)->n]; // pedantic-warning {{zero size arrays are an extension}}
+
+_Static_assert(sizeof(name2) == 0, "");
+
+#ifdef __SIZEOF_INT128__
+void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1];
+#endif
+
+extern float global_float;
+struct XX { int a, *b; };
+struct XY { int before; struct XX xx, *xp; float* after; } xy[] = {
+ 0, 0, &xy[0].xx.a, &xy[0].xx, &global_float,
+ [1].xx = 0, &xy[1].xx.a, &xy[1].xx, &global_float,
+ 0, // all-note {{previous initialization is here}}
+ 0, // all-note {{previous initialization is here}}
+ [2].before = 0, // all-warning {{initializer overrides prior initialization of this subobject}}
+ 0, // all-warning {{initializer overrides prior initialization of this subobject}}
+ &xy[2].xx.a, &xy[2].xx, &global_float
+};
+
+void t14(void) {
+ int array[256] = { 0 }; // expected-note {{array 'array' declared here}} \
+ // pedantic-expected-note {{array 'array' declared here}} \
+ // ref-note {{array 'array' declared here}} \
+ // pedantic-ref-note {{array 'array' declared here}}
+ const char b = -1;
+ int val = array[b]; // expected-warning {{array index -1 is before the beginning of the array}} \
+ // pedantic-expected-warning {{array index -1 is before the beginning of the array}} \
+ // ref-warning {{array index -1 is before the beginning of the array}} \
+ // pedantic-ref-warning {{array index -1 is before the beginning of the array}}
+
+}
+
+void bar_0(void) {
+ struct C {
+ const int a;
+ int b;
+ };
+
+ const struct C S = {0, 0};
+
+ *(int *)(&S.a) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}}
+ *(int *)(&S.b) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}}
+}
+
+/// Complex-to-bool casts.
+const int A = ((_Complex double)1.0 ? 21 : 1);
+_Static_assert(A == 21, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+const int CTI1 = ((_Complex double){0.0, 1.0}); // pedantic-ref-warning {{extension}} \
+ // pedantic-expected-warning {{extension}}
+_Static_assert(CTI1 == 0, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+const _Bool CTB2 = (_Bool)(_Complex double){0.0, 1.0}; // pedantic-ref-warning {{extension}} \
+ // pedantic-expected-warning {{extension}}
+_Static_assert(CTB2, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+const _Bool CTB3 = (_Complex double){0.0, 1.0}; // pedantic-ref-warning {{extension}} \
+ // pedantic-expected-warning {{extension}}
+_Static_assert(CTB3, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+
+void nonComplexToComplexCast(void) {
+ _Complex double z = *(_Complex double *)&(struct { double r, i; }){0.0, 1.0};
+}
+
+int t1 = sizeof(int);
+void test4(void) {
+ t1 = sizeof(int);
+}
+
+void localCompoundLiteral(void) {
+ struct S { int x, y; } s = {}; // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+ struct T {
+ int i;
+ struct S s;
+ } t1 = { 1, {} }; // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+
+ struct T t3 = {
+ (int){}, // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+ {} // pedantic-expected-warning {{use of an empty initializer}} \
+ // pedantic-ref-warning {{use of an empty initializer}}
+ };
+}
+
+/// struct copy
+struct StrA {int a; };
+const struct StrA sa = { 12 };
+const struct StrA * const sb = &sa;
+const struct StrA sc = *sb;
+_Static_assert(sc.a == 12, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+struct ComplexS {
+ int a;
+ float b;
+ struct StrA sa[2];
+};
+const struct ComplexS CS = {12, 23.0f, {{1}, {2}}};
+const struct ComplexS CS2 = CS;
+_Static_assert(CS2.sa[0].a == 1, ""); // pedantic-ref-warning {{GNU extension}} \
+ // pedantic-expected-warning {{GNU extension}}
+
+_Static_assert(((void*)0 + 1) != (void*)0, ""); // pedantic-expected-warning {{arithmetic on a pointer to void is a GNU extension}} \
+ // pedantic-expected-warning {{not an integer constant expression}} \
+ // pedantic-expected-note {{cannot perform pointer arithmetic on null pointer}} \
+ // pedantic-ref-warning {{arithmetic on a pointer to void is a GNU extension}} \
+ // pedantic-ref-warning {{not an integer constant expression}} \
+ // pedantic-ref-note {{cannot perform pointer arithmetic on null pointer}}
+
+typedef __INTPTR_TYPE__ intptr_t;
+int array[(intptr_t)(int*)1]; // ref-warning {{variable length array folded to constant array}} \
+ // pedantic-ref-warning {{variable length array folded to constant array}} \
+ // expected-warning {{variable length array folded to constant array}} \
+ // pedantic-expected-warning {{variable length array folded to constant array}}
+
+int castViaInt[*(int*)(unsigned long)"test"]; // ref-error {{variable length array}} \
+ // pedantic-ref-error {{variable length array}} \
+ // expected-error {{variable length array}} \
+ // pedantic-expected-error {{variable length array}} \
+ // all-warning {{cast to 'int *' from smaller integer type 'unsigned long'}}
+
+const void (*const funcp)(void) = (void*)123; // pedantic-warning {{converts between void pointer and function pointer}} \
+ // pedantic-expected-note {{this conversion is not allowed in a constant expression}}
+_Static_assert(funcp == (void*)0, ""); // all-error {{failed due to requirement 'funcp == (void *)0'}} \
+ // pedantic-warning {{expression is not an integer constant expression}}
+_Static_assert(funcp == (void*)123, ""); // pedantic-warning {{equality comparison between function pointer and void pointer}} \
+ // pedantic-warning {{expression is not an integer constant expression}}
+
+void unaryops(void) {
+ (void)(++(struct x {unsigned x;}){3}.x);
+ (void)(--(struct y {unsigned x;}){3}.x);
+ (void)(++(struct z {float x;}){3}.x);
+ (void)(--(struct w {float x;}){3}.x);
+
+ (void)((struct xx {unsigned x;}){3}.x++);
+ (void)((struct yy {unsigned x;}){3}.x--);
+ (void)((struct zz {float x;}){3}.x++);
+ (void)((struct ww {float x;}){3}.x--);
+}
+
+/// This used to fail because we didn't properly mark the struct
+/// initialized through a CompoundLiteralExpr as initialized.
+struct TestStruct {
+ int a;
+ int b;
+};
+int Y __attribute__((annotate(
+ "GlobalValAnnotationWithArgs",
+ 42,
+ (struct TestStruct) { .a = 1, .b = 2 }
+)));
+
+#ifdef __SIZEOF_INT128__
+const int *p = &b;
+const __int128 K = (__int128)(int*)0;
+const unsigned __int128 KU = (unsigned __int128)(int*)0;
+#endif
+
+
+int test3(void) {
+ int a[2];
+ a[0] = test3; // all-error {{incompatible pointer to integer conversion assigning to 'int' from 'int (void)'}}
+ return 0;
+}
+/// This tests that we have full type info, even for values we cannot read.
+int dummyarray[5];
+_Static_assert(&dummyarray[0] < &dummyarray[1], ""); // pedantic-warning {{GNU extension}}
+
+void addrlabelexpr(void) {
+ a0: ;
+ static void *ps[] = { &&a0 }; // pedantic-warning {{use of GNU address-of-label extension}}
+}
+
+extern void cv2;
+void *foo5 (void)
+{
+ return &cv2; // pedantic-warning{{address of an expression of type 'void'}}
+}
+
+__attribute__((weak)) const unsigned int test10_bound = 10;
+char test10_global[test10_bound]; // all-error {{variable length array declaration not allowed at file scope}}
+void test10(void) {
+ char test10_local[test10_bound] = "help"; // all-error {{variable-sized object may not be initialized}}
+}
+
+void SuperSpecialFunc(void) {
+const int SuperSpecialCase = 10;
+_Static_assert((sizeof(SuperSpecialCase) == 12 && SuperSpecialCase == 3) || SuperSpecialCase == 10, ""); // pedantic-warning {{GNU extension}}
+}
+
+
+void T1(void) {
+ static int *y[1] = {({ static int _x = 20; (void*)0;})}; // all-error {{initializer element is not a compile-time constant}} \
+ // pedantic-warning {{use of GNU statement expression extension}}
+}
+
+enum teste1 test1f(void), (*test1)(void) = test1f; // pedantic-warning {{ISO C forbids forward references to 'enum' types}}
+enum teste1 { TEST1 };
+
+void func(void) {
+ _Static_assert(func + 1 - func == 1, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
+ // pedantic-warning {{arithmetic on pointers to the function type}} \
+ // pedantic-warning {{not an integer constant expression}}
+ _Static_assert(func + 0xdead000000000000UL - 0xdead000000000000UL == func, ""); // pedantic-warning 2{{arithmetic on a pointer to the function type}} \
+ // pedantic-warning {{not an integer constant expression}} \
+ // pedantic-note {{cannot refer to element 16045481047390945280 of non-array object in a constant expression}}
+ _Static_assert(func + 1 != func, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
+ // pedantic-warning {{expression is not an integer constant expression}}
+ func + 0xdead000000000000UL; // all-warning {{expression result unused}} \
+ // pedantic-warning {{arithmetic on a pointer to the function type}}
+ func - 0xdead000000000000UL; // all-warning {{expression result unused}} \
+ // pedantic-warning {{arithmetic on a pointer to the function type}}
+}
+
+void foo3 (void)
+{
+ void* x = 0;
+ void* y = &*x;
+}
+
+static void *FooTable[1] = {
+ [0] = (void *[1]) { // 1
+ [0] = (void *[1]) { // 2
+ [0] = (void *[1]) {} // pedantic-warning {{use of an empty initializer}}
+ },
+ }
+};
+
+int strcmp(const char *, const char *); // all-note {{passing argument to parameter here}}
+#define S "\x01\x02\x03\x04\x05\x06\x07\x08"
+const char _str[] = {S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7]};
+const unsigned char _str2[] = {S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7]};
+const int compared = strcmp(_str, (const char *)_str2); // all-error {{initializer element is not a compile-time constant}}
+
+
+const int compared2 = strcmp(strcmp, _str); // all-error {{incompatible pointer types}} \
+ // all-error {{initializer element is not a compile-time constant}}
+
+int foo(x) // all-warning {{a function definition without a prototype is deprecated in all versions of C}}
+int x;
+{
+ return x;
+}
+
+void bar() { // pedantic-warning {{a function declaration without a prototype}}
+ int x;
+ x = foo(); // all-warning {{too few arguments}}
+}
+
+int *_b = &a;
+void discardedCmp(void)
+{
+ (*_b) = ((&a == &a) , a); // all-warning {{left operand of comma operator has no effect}}
+}
+
+/// ArraySubscriptExpr that's not an lvalue
+typedef unsigned char U __attribute__((vector_size(1)));
+void nonLValueASE(U f) { f[0] = f[((U)(U){0})[0]]; }
+
+static char foo_(a) // all-warning {{definition without a prototype}}
+ char a;
+{
+ return 'a';
+}
+static void bar_(void) {
+ foo_(foo_(1));
+}
+
+void foo2(void*);
+void bar2(void) {
+ int a[2][3][4][5]; // all-note {{array 'a' declared here}}
+ foo2(&a[0][4]); // all-warning {{array index 4 is past the end of the array}}
+}
+
+void plainComplex(void) {
+ _Complex cd; // all-warning {{_Complex double}}
+ cd = *(_Complex *)&(struct { double r, i; }){0.0, 0.0}; // all-warning {{_Complex double}}
+}
+
+/// This test results in an ImplicitValueInitExpr with DiscardResult set.
+struct M{
+ char c;
+};
+typedef struct S64 {
+ struct M m;
+ char a[64];
+} I64;
+
+_Static_assert((((I64){}, 1)), ""); // all-warning {{left operand of comma operator has no effect}} \
+ // pedantic-warning {{use of an empty initializer is a C23 extension}} \
+ // pedantic-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
+
+#define V(N) __attribute__((vector_size(N)))
+#define C2 (VC2){0, 1}
+char func_(void);
+typedef V(2) char VC2;
+void CopyArrayToFnPtr(void) { *(VC2 *)func_ = C2; }
+
+_Complex double returnsComplex(); // pedantic-warning {{a function declaration without a prototype is deprecated in all versions of C}}
+void callReturnsComplex(void) {
+ _Complex double c;
+ c = returnsComplex(0.); // all-warning {{passing arguments to 'returnsComplex' without a prototype is deprecated in all versions of C and is not supported in C23}}
+}
+
+int complexMul[2 * (22222222222wb + 2i) == 2]; // all-warning {{'_BitInt' suffix for literals is a C23 extension}} \
+ // pedantic-warning {{imaginary constants are a C2y extension}} \
+ // all-warning {{variable length array folded to constant array as an extension}}
+
+int complexDiv[2 / (22222222222wb + 2i) == 2]; // all-warning {{'_BitInt' suffix for literals is a C23 extension}} \
+ // pedantic-warning {{imaginary constants are a C2y extension}} \
+ // all-warning {{variable length array folded to constant array as an extension}}
+
+
+
+int i = 0;
+void intPtrCmp1(void) { &i + 1 == 2; } // all-warning {{comparison between pointer and integer}} \
+ // all-warning {{equality comparison result unused}}
+void intPtrCmp2(void) { 2 == &i + 1; } // all-warning {{comparison between pointer and integer}} \
+ // all-warning {{equality comparison result unused}}
+
+/// evaluateStrlen on a non-block pointer.
+char *strcpy(char *restrict s1, const char *restrict s2);
+void strcpy_fn(char *x) {
+ strcpy(x, (char*)&strcpy_fn);
+}
+
+/// strcpy on a non-integer pointer.
+void strcpyDouble(void) {
+ char buf[1];
+ const double test_buf[] = {'4', '2'};
+ __builtin_strcpy(buf, test_buf + 1); // all-error {{incompatible pointer types}}
+}
+
+int *iptr;
+void ignoredConditional(void) { *iptr = (((_Complex double)1.0 ? 2 : 3), a); } // all-warning {{left operand of comma operator has no effect}}
diff --git a/clang/test/CodeGen/MSKernel/containing-record.cpp b/clang/test/CodeGen/MSKernel/containing-record.cpp
new file mode 100644
index 0000000000000..4e8b8e6abc849
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/containing-record.cpp
@@ -0,0 +1,57 @@
+// Normally this file can't be compiled due to
+// nullptr cast in offset calculation
+// RUN: not %clang_cc1 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
+// Kernel variant works OK.
+// RUN: %clang_cc1 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
+
+// AST-ORIG: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
+// AST-ORIG: FunctionDecl
+// AST-ORIG-NEXT: ParmVarDecl
+// AST-ORIG-NEXT: CompoundStmt
+// AST-ORIG-NEXT: ReturnStmt
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: BinaryOperator
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ImplicitCastExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: DeclRefExpr
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: UnaryOperator
+// AST-ORIG-NEXT: MemberExpr
+// AST-ORIG-NEXT: ParenExpr
+// AST-ORIG-NEXT: CStyleCastExpr
+// AST-ORIG-NEXT: ImplicitCastExpr
+// AST-ORIG-NEXT: IntegerLiteral
+
+// AST-NEW: FunctionDecl
+// AST-NEW-NEXT: ParmVarDecl
+// AST-NEW-NEXT: CompoundStmt
+// AST-NEW-NEXT: ReturnStmt
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: BinaryOperator
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ImplicitCastExpr
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: DeclRefExpr
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ImplicitCastExpr
+// AST-NEW-NEXT: OffsetOfExpr
+
+typedef char* PCHAR;
+typedef unsigned long long ULONG_PTR;
+
+#define CONTAINING_RECORD_UB(address, type, field) ((type *)( \
+ (PCHAR)(address) - (ULONG_PTR)(&((type *)0)->field)))
+
+
+struct MyStruct { int a; int b; int c; };
+
+constexpr MyStruct* get_container_ub(int* p) {
+ return CONTAINING_RECORD_UB(p, MyStruct, b);
+}
+
diff --git a/clang/test/CodeGen/MSKernel/nullptr.cl b/clang/test/CodeGen/MSKernel/nullptr.cl
new file mode 100644
index 0000000000000..9b4b66639bd3a
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/nullptr.cl
@@ -0,0 +1,25 @@
+// On amdgcn nullptr is -1. We don't rewrite AST in such case
+// RUN: %clang_cc1 -fms-kernel -no-enable-noundef-analysis %s -cl-std=CL2.0 -include opencl-c.h -triple amdgcn -emit-llvm -o - | FileCheck %s
+
+typedef struct {
+ private char *p1;
+ local char *p2;
+ constant char *p3;
+ global char *p4;
+ generic char *p5;
+} StructTy1;
+
+typedef struct {
+ constant char *p3;
+ global char *p4;
+ generic char *p5;
+} StructTy2;
+
+
+// CHECK: @fold_int5 ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
+int fold_int5 = (int) &((private StructTy1*)0)->p2;
+
+// CHECK: @fold_int5_local ={{.*}} local_unnamed_addr addrspace(1) global i32 3, align 4
+int fold_int5_local = (int) &((local StructTy1*)0)->p2;
+
+
diff --git a/clang/test/CodeGen/MSKernel/offsetof.cpp b/clang/test/CodeGen/MSKernel/offsetof.cpp
new file mode 100644
index 0000000000000..864a78a70d642
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/offsetof.cpp
@@ -0,0 +1,39 @@
+// Check that sum_offsets1 and sum_offsets2 are identical
+// with and without -fms-kernel
+// RUN: %clang_cc1 -fms-kernel -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -fms-extensions -O2 -triple x86_64-windows-msvc -emit-llvm %s -o - | FileCheck %s
+
+// CHECK: define dso_local noundef i64 @"?sum_offsets1@@YA_KXZ"()
+// CHECK-NEXT: entry:
+// CHECK-NEXT: ret i64 632
+
+// CHECK: define dso_local noundef i64 @"?sum_offsets2@@YA_KXZ"()
+// CHECK-NEXT: entry:
+// CHECK-NEXT ret i64 632
+
+typedef unsigned long long ULONG_PTR;
+
+#define MY_OFFSETOF(type, field) (ULONG_PTR)(&((type *)0)->field)
+
+namespace foo {
+typedef struct MyStruct {
+ char b;
+ struct X {
+ long m0[10], m1;
+ } x[10];
+ long c;
+} MyStruct;
+}
+
+__declspec(noinline) ULONG_PTR sum_offsets1() {
+ return MY_OFFSETOF(foo::MyStruct, x[1].m0[2]) +
+ MY_OFFSETOF(foo::MyStruct, x[2].m1) +
+ MY_OFFSETOF(foo::MyStruct, c);
+}
+
+__declspec(noinline) ULONG_PTR sum_offsets2() {
+ return __builtin_offsetof(foo::MyStruct, x[1].m0[2]) +
+ __builtin_offsetof(foo::MyStruct, x[2].m1) +
+ __builtin_offsetof(foo::MyStruct, c);
+}
+
diff --git a/clang/test/CodeGen/MSKernel/struct-decl.c b/clang/test/CodeGen/MSKernel/struct-decl.c
new file mode 100644
index 0000000000000..b6b25bd3f1c95
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/struct-decl.c
@@ -0,0 +1,114 @@
+// This one of two tests which failed when LLVM test suite
+// Replacing offsetof with __builtin_offsetof slightly affects
+// warning messages. Compare with Sema/struct-decl.c
+// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -Wno-pointer-to-int-cast -fsyntax-only -verify %s
+// PR3459
+struct bar {
+ char n[1];
+};
+
+struct foo {
+ char name[(int)&((struct bar *)0)->n];
+ char name2[(int)&((struct bar *)0)->n - 1]; // expected-error {{'name2' declared as an array with a negative size}}
+};
+
+// PR3430
+struct s {
+ struct st {
+ int v;
+ } *ts;
+};
+
+struct st;
+
+int foo(void) {
+ struct st *f;
+ return f->v + f[0].v;
+}
+
+// PR3642, PR3671
+struct pppoe_tag {
+ short tag_type;
+ char tag_data[];
+};
+struct datatag {
+ struct pppoe_tag hdr; //expected-warning{{field 'hdr' with variable sized type 'struct pppoe_tag' not at the end of a struct or class is a GNU extension}}
+ char data;
+};
+
+
+// PR4092
+struct s0 {
+ char a; // expected-note {{previous declaration is here}}
+ char a; // expected-error {{duplicate member 'a'}}
+};
+
+struct s0 f0(void) {}
+
+// This previously triggered an assertion failure.
+struct x0 {
+ unsigned int x1;
+};
+
+static struct test1 { // expected-warning {{'static' ignored on this declaration}}
+ int x;
+};
+const struct test2 { // expected-warning {{'const' ignored on this declaration}}
+ int x;
+};
+inline struct test3 { // expected-error {{'inline' can only appear on functions}}
+ int x;
+};
+
+struct hiding_1 {};
+struct hiding_2 {};
+void test_hiding(void) {
+ struct hiding_1 *hiding_1(void);
+ extern struct hiding_2 *hiding_2;
+ struct hiding_1 *p = hiding_1();
+ struct hiding_2 *q = hiding_2;
+}
+
+struct PreserveAttributes {};
+typedef struct __attribute__((noreturn)) PreserveAttributes PreserveAttributes_t; // expected-warning {{'noreturn' attribute only applies to functions and methods}}
+
+// PR46255
+struct FlexibleArrayMem {
+ int a;
+ int b[];
+};
+
+struct FollowedByNamed {
+ struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
+ int i;
+};
+
+struct FollowedByUnNamed {
+ struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
+ struct {
+ int i;
+ };
+};
+
+struct InAnonymous {
+ struct { // expected-warning-re {{field '' with variable sized type 'struct InAnonymous::(anonymous at {{.+}})' not at the end of a struct or class is a GNU extension}}
+
+ struct FlexibleArrayMem a;
+ };
+ int i;
+};
+struct InAnonymousFollowedByAnon {
+ struct { // expected-warning-re {{field '' with variable sized type 'struct InAnonymousFollowedByAnon::(anonymous at {{.+}})' not at the end of a struct or class is a GNU extension}}
+
+ struct FlexibleArrayMem a;
+ };
+ struct {
+ int i;
+ };
+};
+
+// This is the behavior in C++ as well, so making sure we reproduce it here.
+struct InAnonymousFollowedByEmpty {
+ struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
+ struct {};
+};
>From 4dfad37730a65de767d824b22b0a845581370218 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Mon, 27 Apr 2026 12:00:43 +0200
Subject: [PATCH 2/4] Define triple to fix builder failure
---
clang/test/CodeGen/MSKernel/containing-record.cpp | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/clang/test/CodeGen/MSKernel/containing-record.cpp b/clang/test/CodeGen/MSKernel/containing-record.cpp
index 4e8b8e6abc849..54f4482ed73cf 100644
--- a/clang/test/CodeGen/MSKernel/containing-record.cpp
+++ b/clang/test/CodeGen/MSKernel/containing-record.cpp
@@ -1,8 +1,8 @@
// Normally this file can't be compiled due to
// nullptr cast in offset calculation
-// RUN: not %clang_cc1 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
+// RUN: not %clang_cc1 -triple x86_64-pc-win32 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
// Kernel variant works OK.
-// RUN: %clang_cc1 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
+// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
// AST-ORIG: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
// AST-ORIG: FunctionDecl
@@ -39,7 +39,6 @@
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: DeclRefExpr
// AST-NEW-NEXT: CStyleCastExpr
-// AST-NEW-NEXT: ImplicitCastExpr
// AST-NEW-NEXT: OffsetOfExpr
typedef char* PCHAR;
>From 5fe1e6d5b49b025092d2fd94d03e1d3914682aae Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Mon, 27 Apr 2026 19:24:01 +0200
Subject: [PATCH 3/4] Switch to using PseudoObjectExpr
This allows us reconstructing source code from AST in an expected way
and also fixes the compiler warnings, so some tests were removed.
---
clang/lib/Sema/SemaCast.cpp | 23 +-
clang/lib/Sema/SemaMSKernel.cpp | 2 +-
clang/test/CodeGen/MSKernel/ast-bytecode-c.c | 455 ------------------
.../CodeGen/MSKernel/containing-record.cpp | 19 +-
clang/test/CodeGen/MSKernel/struct-decl.c | 114 -----
5 files changed, 34 insertions(+), 579 deletions(-)
delete mode 100644 clang/test/CodeGen/MSKernel/ast-bytecode-c.c
delete mode 100644 clang/test/CodeGen/MSKernel/struct-decl.c
diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp
index 589c978d82248..fd3a66062f2dd 100644
--- a/clang/lib/Sema/SemaCast.cpp
+++ b/clang/lib/Sema/SemaCast.cpp
@@ -3478,15 +3478,24 @@ ExprResult Sema::BuildCStyleCastExpr(SourceLocation LPLoc,
Op.checkQualifiedDestType();
- if (Op.Kind == CK_PointerToIntegral && Context.getLangOpts().Kernel) {
- ExprResult Transformed =
- TransformForMSKernel(Op.SrcExpr.get()->IgnoreParenImpCasts());
- if (Transformed.isUsable())
- return BuildCStyleCastExpr(LPLoc, CastTypeInfo, RPLoc, Transformed.get());
- }
- return Op.complete(CStyleCastExpr::Create(
+ ExprResult Cast = Op.complete(CStyleCastExpr::Create(
Context, Op.ResultType, Op.ValueKind, Op.Kind, Op.SrcExpr.get(),
&Op.BasePath, CurFPFeatureOverrides(), CastTypeInfo, LPLoc, RPLoc));
+ if (!Cast.isUsable() || Op.Kind != CK_PointerToIntegral ||
+ !Context.getLangOpts().Kernel)
+ return Cast;
+
+ ExprResult Transformed =
+ TransformForMSKernel(Op.SrcExpr.get()->IgnoreParenImpCasts());
+ if (!Transformed.isUsable())
+ return Cast;
+
+ ExprResult NewCast =
+ BuildCStyleCastExpr(LPLoc, CastTypeInfo, RPLoc, Transformed.get());
+ if (!NewCast.isUsable())
+ return NewCast;
+ Expr *RealExpr = NewCast.get();
+ return PseudoObjectExpr::Create(Context, Cast.get(), {&RealExpr, 1}, 0);
}
ExprResult Sema::BuildCXXFunctionalCastExpr(TypeSourceInfo *CastTypeInfo,
diff --git a/clang/lib/Sema/SemaMSKernel.cpp b/clang/lib/Sema/SemaMSKernel.cpp
index 6d70ebf4411ac..573cd7ab529db 100644
--- a/clang/lib/Sema/SemaMSKernel.cpp
+++ b/clang/lib/Sema/SemaMSKernel.cpp
@@ -51,7 +51,7 @@ ExprResult TransformUnaryOperator(Sema &SemaRef, UnaryOperator *UO) {
return {};
std::reverse(Components.begin(), Components.end());
- // Get the root structure type (e.g., MyStruct)
+ // Get the root structure type
TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(
Current->getType()->getPointeeType(), Current->getBeginLoc());
return SemaRef.BuildBuiltinOffsetOf(UO->getBeginLoc(), TInfo, Components,
diff --git a/clang/test/CodeGen/MSKernel/ast-bytecode-c.c b/clang/test/CodeGen/MSKernel/ast-bytecode-c.c
deleted file mode 100644
index c83feccd4d486..0000000000000
--- a/clang/test/CodeGen/MSKernel/ast-bytecode-c.c
+++ /dev/null
@@ -1,455 +0,0 @@
-// This test is a modified version of AST/ByteCode/c.c
-// Original test fails in case entire LLVM test suite is compiled with -fms-kernel
-// Changes are minor and reflect differences in handling of __builtin_offsetof vs
-// offsetof and long type size in Windows vs Linux (4 vs 8)
-// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=expected,all -std=c11 -Wcast-qual %s -fexperimental-new-constant-interpreter
-// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=pedantic,pedantic-expected,all -std=c11 -Wcast-qual -pedantic %s -fexperimental-new-constant-interpreter
-// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=ref,all -std=c11 -Wcast-qual %s
-// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -verify=pedantic,pedantic-ref,all -std=c11 -Wcast-qual -pedantic %s
-
-typedef __INTPTR_TYPE__ intptr_t;
-typedef __PTRDIFF_TYPE__ ptrdiff_t;
-
-_Static_assert(1, "");
-
-_Static_assert(__objc_yes, "");
-_Static_assert(!__objc_no, "");
-
-_Static_assert(0 != 1, "");
-_Static_assert(1.0 == 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
- // pedantic-expected-warning {{not an integer constant expression}}
-_Static_assert(1 && 1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
- // pedantic-expected-warning {{not an integer constant expression}}
-_Static_assert( (5 > 4) + (3 > 2) == 2, "");
-_Static_assert(!!1.0, ""); // pedantic-ref-warning {{not an integer constant expression}} \
- // pedantic-expected-warning {{not an integer constant expression}}
-_Static_assert(!!1, "");
-
-_Static_assert(!(_Bool){(void*)0}, ""); // pedantic-ref-warning {{not an integer constant expression}} \
- // pedantic-expected-warning {{not an integer constant expression}}
-
-int a = (1 == 1 ? 5 : 3);
-_Static_assert(a == 5, ""); // all-error {{not an integral constant expression}}
-
-const int DiscardedPtrToIntCast = ((intptr_t)((void*)0), 0); // all-warning {{left operand of comma operator has no effect}}
-
-const int b = 3;
-_Static_assert(b == 3, ""); // pedantic-ref-warning {{not an integer constant expression}} \
- // pedantic-expected-warning {{not an integer constant expression}}
-
-const int c; // all-note {{declared here}}
-_Static_assert(c == 0, ""); // ref-error {{not an integral constant expression}} \
- // ref-note {{initializer of 'c' is unknown}} \
- // pedantic-ref-error {{not an integral constant expression}} \
- // pedantic-ref-note {{initializer of 'c' is unknown}} \
- // expected-error {{not an integral constant expression}} \
- // expected-note {{initializer of 'c' is unknown}} \
- // pedantic-expected-error {{not an integral constant expression}} \
- // pedantic-expected-note {{initializer of 'c' is unknown}}
-
-_Static_assert(&c != 0, ""); // ref-warning {{always true}} \
- // pedantic-ref-warning {{always true}} \
- // pedantic-ref-warning {{is a GNU extension}} \
- // expected-warning {{always true}} \
- // pedantic-expected-warning {{always true}} \
- // pedantic-expected-warning {{is a GNU extension}}
-_Static_assert(&a != 0, ""); // ref-warning {{always true}} \
- // pedantic-ref-warning {{always true}} \
- // pedantic-ref-warning {{is a GNU extension}} \
- // expected-warning {{always true}} \
- // pedantic-expected-warning {{always true}} \
- // pedantic-expected-warning {{is a GNU extension}}
-_Static_assert((&c + 1) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
- // pedantic-expected-warning {{is a GNU extension}}
-_Static_assert((&a + 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
- // pedantic-ref-note {{100 of non-array}} \
- // pedantic-expected-note {{100 of non-array}} \
- // pedantic-expected-warning {{is a GNU extension}}
-_Static_assert((&a - 100) != 0, ""); // pedantic-ref-warning {{is a GNU extension}} \
- // pedantic-expected-warning {{is a GNU extension}} \
- // pedantic-ref-note {{-100 of non-array}} \
- // pedantic-expected-note {{-100 of non-array}}
-/// extern variable of a composite type.
-extern struct Test50S Test50;
-_Static_assert(&Test50 != (void*)0, ""); // all-warning {{always true}} \
- // pedantic-warning {{is a GNU extension}} \
- // pedantic-note {{this conversion is not allowed in a constant expression}}
-
-struct y {int x,y;};
-int a2[(intptr_t)&((struct y*)0)->y];
-
-const struct y *yy = (struct y*)0;
-const intptr_t L = (intptr_t)(&(yy->y)); // all-error {{not a compile-time constant}}
-
-_Static_assert((long)&((struct y*)0)->y > 0, ""); // all-warning {{cast to smaller integer type 'long' from 'int *'}}
-
-const ptrdiff_t m = &m + 137 - &m;
-_Static_assert(m == 137, ""); // pedantic-ref-warning {{GNU extension}} \
- // pedantic-expected-warning {{GNU extension}}
-
-/// from test/Sema/switch.c, used to cause an assertion failure.
-void f (int z) {
- while (z) {
- default: z--; // all-error {{'default' statement not in switch}}
- }
-}
-
-int expr;
-int chooseexpr[__builtin_choose_expr(1, 1, expr)];
-
-int somefunc(int i) {
- return (i, 65537) * 65537; // all-warning {{left operand of comma operator has no effect}} \
- // all-warning {{overflow in expression; result is 131'073 with type 'int'}}
-}
-
-#pragma clang diagnostic ignored "-Wpointer-to-int-cast"
-struct ArrayStruct {
- char n[1];
-};
-
-// With -fms-kernel we replace offsetof expression with __builtin_offsetof
-// This eliminates original warning (folded to constant array), but
-// introduces another one (zero size array is an extension)
-char name2[(int)&((struct ArrayStruct*)0)->n]; // pedantic-warning {{zero size arrays are an extension}}
-
-_Static_assert(sizeof(name2) == 0, "");
-
-#ifdef __SIZEOF_INT128__
-void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1];
-#endif
-
-extern float global_float;
-struct XX { int a, *b; };
-struct XY { int before; struct XX xx, *xp; float* after; } xy[] = {
- 0, 0, &xy[0].xx.a, &xy[0].xx, &global_float,
- [1].xx = 0, &xy[1].xx.a, &xy[1].xx, &global_float,
- 0, // all-note {{previous initialization is here}}
- 0, // all-note {{previous initialization is here}}
- [2].before = 0, // all-warning {{initializer overrides prior initialization of this subobject}}
- 0, // all-warning {{initializer overrides prior initialization of this subobject}}
- &xy[2].xx.a, &xy[2].xx, &global_float
-};
-
-void t14(void) {
- int array[256] = { 0 }; // expected-note {{array 'array' declared here}} \
- // pedantic-expected-note {{array 'array' declared here}} \
- // ref-note {{array 'array' declared here}} \
- // pedantic-ref-note {{array 'array' declared here}}
- const char b = -1;
- int val = array[b]; // expected-warning {{array index -1 is before the beginning of the array}} \
- // pedantic-expected-warning {{array index -1 is before the beginning of the array}} \
- // ref-warning {{array index -1 is before the beginning of the array}} \
- // pedantic-ref-warning {{array index -1 is before the beginning of the array}}
-
-}
-
-void bar_0(void) {
- struct C {
- const int a;
- int b;
- };
-
- const struct C S = {0, 0};
-
- *(int *)(&S.a) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}}
- *(int *)(&S.b) = 0; // all-warning {{cast from 'const int *' to 'int *' drops const qualifier}}
-}
-
-/// Complex-to-bool casts.
-const int A = ((_Complex double)1.0 ? 21 : 1);
-_Static_assert(A == 21, ""); // pedantic-ref-warning {{GNU extension}} \
- // pedantic-expected-warning {{GNU extension}}
-
-const int CTI1 = ((_Complex double){0.0, 1.0}); // pedantic-ref-warning {{extension}} \
- // pedantic-expected-warning {{extension}}
-_Static_assert(CTI1 == 0, ""); // pedantic-ref-warning {{GNU extension}} \
- // pedantic-expected-warning {{GNU extension}}
-
-const _Bool CTB2 = (_Bool)(_Complex double){0.0, 1.0}; // pedantic-ref-warning {{extension}} \
- // pedantic-expected-warning {{extension}}
-_Static_assert(CTB2, ""); // pedantic-ref-warning {{GNU extension}} \
- // pedantic-expected-warning {{GNU extension}}
-
-const _Bool CTB3 = (_Complex double){0.0, 1.0}; // pedantic-ref-warning {{extension}} \
- // pedantic-expected-warning {{extension}}
-_Static_assert(CTB3, ""); // pedantic-ref-warning {{GNU extension}} \
- // pedantic-expected-warning {{GNU extension}}
-
-
-void nonComplexToComplexCast(void) {
- _Complex double z = *(_Complex double *)&(struct { double r, i; }){0.0, 1.0};
-}
-
-int t1 = sizeof(int);
-void test4(void) {
- t1 = sizeof(int);
-}
-
-void localCompoundLiteral(void) {
- struct S { int x, y; } s = {}; // pedantic-expected-warning {{use of an empty initializer}} \
- // pedantic-ref-warning {{use of an empty initializer}}
- struct T {
- int i;
- struct S s;
- } t1 = { 1, {} }; // pedantic-expected-warning {{use of an empty initializer}} \
- // pedantic-ref-warning {{use of an empty initializer}}
-
- struct T t3 = {
- (int){}, // pedantic-expected-warning {{use of an empty initializer}} \
- // pedantic-ref-warning {{use of an empty initializer}}
- {} // pedantic-expected-warning {{use of an empty initializer}} \
- // pedantic-ref-warning {{use of an empty initializer}}
- };
-}
-
-/// struct copy
-struct StrA {int a; };
-const struct StrA sa = { 12 };
-const struct StrA * const sb = &sa;
-const struct StrA sc = *sb;
-_Static_assert(sc.a == 12, ""); // pedantic-ref-warning {{GNU extension}} \
- // pedantic-expected-warning {{GNU extension}}
-
-struct ComplexS {
- int a;
- float b;
- struct StrA sa[2];
-};
-const struct ComplexS CS = {12, 23.0f, {{1}, {2}}};
-const struct ComplexS CS2 = CS;
-_Static_assert(CS2.sa[0].a == 1, ""); // pedantic-ref-warning {{GNU extension}} \
- // pedantic-expected-warning {{GNU extension}}
-
-_Static_assert(((void*)0 + 1) != (void*)0, ""); // pedantic-expected-warning {{arithmetic on a pointer to void is a GNU extension}} \
- // pedantic-expected-warning {{not an integer constant expression}} \
- // pedantic-expected-note {{cannot perform pointer arithmetic on null pointer}} \
- // pedantic-ref-warning {{arithmetic on a pointer to void is a GNU extension}} \
- // pedantic-ref-warning {{not an integer constant expression}} \
- // pedantic-ref-note {{cannot perform pointer arithmetic on null pointer}}
-
-typedef __INTPTR_TYPE__ intptr_t;
-int array[(intptr_t)(int*)1]; // ref-warning {{variable length array folded to constant array}} \
- // pedantic-ref-warning {{variable length array folded to constant array}} \
- // expected-warning {{variable length array folded to constant array}} \
- // pedantic-expected-warning {{variable length array folded to constant array}}
-
-int castViaInt[*(int*)(unsigned long)"test"]; // ref-error {{variable length array}} \
- // pedantic-ref-error {{variable length array}} \
- // expected-error {{variable length array}} \
- // pedantic-expected-error {{variable length array}} \
- // all-warning {{cast to 'int *' from smaller integer type 'unsigned long'}}
-
-const void (*const funcp)(void) = (void*)123; // pedantic-warning {{converts between void pointer and function pointer}} \
- // pedantic-expected-note {{this conversion is not allowed in a constant expression}}
-_Static_assert(funcp == (void*)0, ""); // all-error {{failed due to requirement 'funcp == (void *)0'}} \
- // pedantic-warning {{expression is not an integer constant expression}}
-_Static_assert(funcp == (void*)123, ""); // pedantic-warning {{equality comparison between function pointer and void pointer}} \
- // pedantic-warning {{expression is not an integer constant expression}}
-
-void unaryops(void) {
- (void)(++(struct x {unsigned x;}){3}.x);
- (void)(--(struct y {unsigned x;}){3}.x);
- (void)(++(struct z {float x;}){3}.x);
- (void)(--(struct w {float x;}){3}.x);
-
- (void)((struct xx {unsigned x;}){3}.x++);
- (void)((struct yy {unsigned x;}){3}.x--);
- (void)((struct zz {float x;}){3}.x++);
- (void)((struct ww {float x;}){3}.x--);
-}
-
-/// This used to fail because we didn't properly mark the struct
-/// initialized through a CompoundLiteralExpr as initialized.
-struct TestStruct {
- int a;
- int b;
-};
-int Y __attribute__((annotate(
- "GlobalValAnnotationWithArgs",
- 42,
- (struct TestStruct) { .a = 1, .b = 2 }
-)));
-
-#ifdef __SIZEOF_INT128__
-const int *p = &b;
-const __int128 K = (__int128)(int*)0;
-const unsigned __int128 KU = (unsigned __int128)(int*)0;
-#endif
-
-
-int test3(void) {
- int a[2];
- a[0] = test3; // all-error {{incompatible pointer to integer conversion assigning to 'int' from 'int (void)'}}
- return 0;
-}
-/// This tests that we have full type info, even for values we cannot read.
-int dummyarray[5];
-_Static_assert(&dummyarray[0] < &dummyarray[1], ""); // pedantic-warning {{GNU extension}}
-
-void addrlabelexpr(void) {
- a0: ;
- static void *ps[] = { &&a0 }; // pedantic-warning {{use of GNU address-of-label extension}}
-}
-
-extern void cv2;
-void *foo5 (void)
-{
- return &cv2; // pedantic-warning{{address of an expression of type 'void'}}
-}
-
-__attribute__((weak)) const unsigned int test10_bound = 10;
-char test10_global[test10_bound]; // all-error {{variable length array declaration not allowed at file scope}}
-void test10(void) {
- char test10_local[test10_bound] = "help"; // all-error {{variable-sized object may not be initialized}}
-}
-
-void SuperSpecialFunc(void) {
-const int SuperSpecialCase = 10;
-_Static_assert((sizeof(SuperSpecialCase) == 12 && SuperSpecialCase == 3) || SuperSpecialCase == 10, ""); // pedantic-warning {{GNU extension}}
-}
-
-
-void T1(void) {
- static int *y[1] = {({ static int _x = 20; (void*)0;})}; // all-error {{initializer element is not a compile-time constant}} \
- // pedantic-warning {{use of GNU statement expression extension}}
-}
-
-enum teste1 test1f(void), (*test1)(void) = test1f; // pedantic-warning {{ISO C forbids forward references to 'enum' types}}
-enum teste1 { TEST1 };
-
-void func(void) {
- _Static_assert(func + 1 - func == 1, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
- // pedantic-warning {{arithmetic on pointers to the function type}} \
- // pedantic-warning {{not an integer constant expression}}
- _Static_assert(func + 0xdead000000000000UL - 0xdead000000000000UL == func, ""); // pedantic-warning 2{{arithmetic on a pointer to the function type}} \
- // pedantic-warning {{not an integer constant expression}} \
- // pedantic-note {{cannot refer to element 16045481047390945280 of non-array object in a constant expression}}
- _Static_assert(func + 1 != func, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
- // pedantic-warning {{expression is not an integer constant expression}}
- func + 0xdead000000000000UL; // all-warning {{expression result unused}} \
- // pedantic-warning {{arithmetic on a pointer to the function type}}
- func - 0xdead000000000000UL; // all-warning {{expression result unused}} \
- // pedantic-warning {{arithmetic on a pointer to the function type}}
-}
-
-void foo3 (void)
-{
- void* x = 0;
- void* y = &*x;
-}
-
-static void *FooTable[1] = {
- [0] = (void *[1]) { // 1
- [0] = (void *[1]) { // 2
- [0] = (void *[1]) {} // pedantic-warning {{use of an empty initializer}}
- },
- }
-};
-
-int strcmp(const char *, const char *); // all-note {{passing argument to parameter here}}
-#define S "\x01\x02\x03\x04\x05\x06\x07\x08"
-const char _str[] = {S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7]};
-const unsigned char _str2[] = {S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7]};
-const int compared = strcmp(_str, (const char *)_str2); // all-error {{initializer element is not a compile-time constant}}
-
-
-const int compared2 = strcmp(strcmp, _str); // all-error {{incompatible pointer types}} \
- // all-error {{initializer element is not a compile-time constant}}
-
-int foo(x) // all-warning {{a function definition without a prototype is deprecated in all versions of C}}
-int x;
-{
- return x;
-}
-
-void bar() { // pedantic-warning {{a function declaration without a prototype}}
- int x;
- x = foo(); // all-warning {{too few arguments}}
-}
-
-int *_b = &a;
-void discardedCmp(void)
-{
- (*_b) = ((&a == &a) , a); // all-warning {{left operand of comma operator has no effect}}
-}
-
-/// ArraySubscriptExpr that's not an lvalue
-typedef unsigned char U __attribute__((vector_size(1)));
-void nonLValueASE(U f) { f[0] = f[((U)(U){0})[0]]; }
-
-static char foo_(a) // all-warning {{definition without a prototype}}
- char a;
-{
- return 'a';
-}
-static void bar_(void) {
- foo_(foo_(1));
-}
-
-void foo2(void*);
-void bar2(void) {
- int a[2][3][4][5]; // all-note {{array 'a' declared here}}
- foo2(&a[0][4]); // all-warning {{array index 4 is past the end of the array}}
-}
-
-void plainComplex(void) {
- _Complex cd; // all-warning {{_Complex double}}
- cd = *(_Complex *)&(struct { double r, i; }){0.0, 0.0}; // all-warning {{_Complex double}}
-}
-
-/// This test results in an ImplicitValueInitExpr with DiscardResult set.
-struct M{
- char c;
-};
-typedef struct S64 {
- struct M m;
- char a[64];
-} I64;
-
-_Static_assert((((I64){}, 1)), ""); // all-warning {{left operand of comma operator has no effect}} \
- // pedantic-warning {{use of an empty initializer is a C23 extension}} \
- // pedantic-warning {{expression is not an integer constant expression; folding it to a constant is a GNU extension}}
-
-#define V(N) __attribute__((vector_size(N)))
-#define C2 (VC2){0, 1}
-char func_(void);
-typedef V(2) char VC2;
-void CopyArrayToFnPtr(void) { *(VC2 *)func_ = C2; }
-
-_Complex double returnsComplex(); // pedantic-warning {{a function declaration without a prototype is deprecated in all versions of C}}
-void callReturnsComplex(void) {
- _Complex double c;
- c = returnsComplex(0.); // all-warning {{passing arguments to 'returnsComplex' without a prototype is deprecated in all versions of C and is not supported in C23}}
-}
-
-int complexMul[2 * (22222222222wb + 2i) == 2]; // all-warning {{'_BitInt' suffix for literals is a C23 extension}} \
- // pedantic-warning {{imaginary constants are a C2y extension}} \
- // all-warning {{variable length array folded to constant array as an extension}}
-
-int complexDiv[2 / (22222222222wb + 2i) == 2]; // all-warning {{'_BitInt' suffix for literals is a C23 extension}} \
- // pedantic-warning {{imaginary constants are a C2y extension}} \
- // all-warning {{variable length array folded to constant array as an extension}}
-
-
-
-int i = 0;
-void intPtrCmp1(void) { &i + 1 == 2; } // all-warning {{comparison between pointer and integer}} \
- // all-warning {{equality comparison result unused}}
-void intPtrCmp2(void) { 2 == &i + 1; } // all-warning {{comparison between pointer and integer}} \
- // all-warning {{equality comparison result unused}}
-
-/// evaluateStrlen on a non-block pointer.
-char *strcpy(char *restrict s1, const char *restrict s2);
-void strcpy_fn(char *x) {
- strcpy(x, (char*)&strcpy_fn);
-}
-
-/// strcpy on a non-integer pointer.
-void strcpyDouble(void) {
- char buf[1];
- const double test_buf[] = {'4', '2'};
- __builtin_strcpy(buf, test_buf + 1); // all-error {{incompatible pointer types}}
-}
-
-int *iptr;
-void ignoredConditional(void) { *iptr = (((_Complex double)1.0 ? 2 : 3), a); } // all-warning {{left operand of comma operator has no effect}}
diff --git a/clang/test/CodeGen/MSKernel/containing-record.cpp b/clang/test/CodeGen/MSKernel/containing-record.cpp
index 54f4482ed73cf..e6b41f86f2f71 100644
--- a/clang/test/CodeGen/MSKernel/containing-record.cpp
+++ b/clang/test/CodeGen/MSKernel/containing-record.cpp
@@ -1,8 +1,10 @@
// Normally this file can't be compiled due to
// nullptr cast in offset calculation
-// RUN: not %clang_cc1 -triple x86_64-pc-win32 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
+// RUN: not %clang_cc1 -triple x86_64-pc-win32 -ast-dump %s -o - 2>&1 | FileCheck %s --check-prefix=AST-ORIG
// Kernel variant works OK.
-// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
+// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-dump %s -o - | FileCheck %s --check-prefix=AST-NEW
+// We use PseudoObjectExpr, so reconstruction of source from AST should work as expected
+// RUN: %clang_cc1 -triple x86_64-pc-win32 -fms-kernel -ast-print %s -o - | FileCheck %s --check-prefix=AST-PRINT
// AST-ORIG: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
// AST-ORIG: FunctionDecl
@@ -38,9 +40,22 @@
// AST-NEW-NEXT: ImplicitCastExpr
// AST-NEW-NEXT: ParenExpr
// AST-NEW-NEXT: DeclRefExpr
+// AST-NEW-NEXT: PseudoObjectExpr
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: UnaryOperator
+// AST-NEW-NEXT: MemberExpr
+// AST-NEW-NEXT: ParenExpr
+// AST-NEW-NEXT: CStyleCastExpr
+// AST-NEW-NEXT: ImplicitCastExpr
+// AST-NEW-NEXT: IntegerLiteral
// AST-NEW-NEXT: CStyleCastExpr
// AST-NEW-NEXT: OffsetOfExpr
+// AST-PRINT: constexpr MyStruct *get_container_ub(int *p) {
+// AST-PRINT-NEXT: return ((MyStruct *)((PCHAR)(p) - (ULONG_PTR)(&((MyStruct *)0)->b)));
+// AST-PRINT-NEXT: }
+
typedef char* PCHAR;
typedef unsigned long long ULONG_PTR;
diff --git a/clang/test/CodeGen/MSKernel/struct-decl.c b/clang/test/CodeGen/MSKernel/struct-decl.c
deleted file mode 100644
index b6b25bd3f1c95..0000000000000
--- a/clang/test/CodeGen/MSKernel/struct-decl.c
+++ /dev/null
@@ -1,114 +0,0 @@
-// This one of two tests which failed when LLVM test suite
-// Replacing offsetof with __builtin_offsetof slightly affects
-// warning messages. Compare with Sema/struct-decl.c
-// RUN: %clang_cc1 -fms-kernel -triple x86_64-windows-msvc -Wno-pointer-to-int-cast -fsyntax-only -verify %s
-// PR3459
-struct bar {
- char n[1];
-};
-
-struct foo {
- char name[(int)&((struct bar *)0)->n];
- char name2[(int)&((struct bar *)0)->n - 1]; // expected-error {{'name2' declared as an array with a negative size}}
-};
-
-// PR3430
-struct s {
- struct st {
- int v;
- } *ts;
-};
-
-struct st;
-
-int foo(void) {
- struct st *f;
- return f->v + f[0].v;
-}
-
-// PR3642, PR3671
-struct pppoe_tag {
- short tag_type;
- char tag_data[];
-};
-struct datatag {
- struct pppoe_tag hdr; //expected-warning{{field 'hdr' with variable sized type 'struct pppoe_tag' not at the end of a struct or class is a GNU extension}}
- char data;
-};
-
-
-// PR4092
-struct s0 {
- char a; // expected-note {{previous declaration is here}}
- char a; // expected-error {{duplicate member 'a'}}
-};
-
-struct s0 f0(void) {}
-
-// This previously triggered an assertion failure.
-struct x0 {
- unsigned int x1;
-};
-
-static struct test1 { // expected-warning {{'static' ignored on this declaration}}
- int x;
-};
-const struct test2 { // expected-warning {{'const' ignored on this declaration}}
- int x;
-};
-inline struct test3 { // expected-error {{'inline' can only appear on functions}}
- int x;
-};
-
-struct hiding_1 {};
-struct hiding_2 {};
-void test_hiding(void) {
- struct hiding_1 *hiding_1(void);
- extern struct hiding_2 *hiding_2;
- struct hiding_1 *p = hiding_1();
- struct hiding_2 *q = hiding_2;
-}
-
-struct PreserveAttributes {};
-typedef struct __attribute__((noreturn)) PreserveAttributes PreserveAttributes_t; // expected-warning {{'noreturn' attribute only applies to functions and methods}}
-
-// PR46255
-struct FlexibleArrayMem {
- int a;
- int b[];
-};
-
-struct FollowedByNamed {
- struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
- int i;
-};
-
-struct FollowedByUnNamed {
- struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
- struct {
- int i;
- };
-};
-
-struct InAnonymous {
- struct { // expected-warning-re {{field '' with variable sized type 'struct InAnonymous::(anonymous at {{.+}})' not at the end of a struct or class is a GNU extension}}
-
- struct FlexibleArrayMem a;
- };
- int i;
-};
-struct InAnonymousFollowedByAnon {
- struct { // expected-warning-re {{field '' with variable sized type 'struct InAnonymousFollowedByAnon::(anonymous at {{.+}})' not at the end of a struct or class is a GNU extension}}
-
- struct FlexibleArrayMem a;
- };
- struct {
- int i;
- };
-};
-
-// This is the behavior in C++ as well, so making sure we reproduce it here.
-struct InAnonymousFollowedByEmpty {
- struct FlexibleArrayMem a; // expected-warning {{field 'a' with variable sized type 'struct FlexibleArrayMem' not at the end of a struct or class is a GNU extension}}
- struct {};
-};
>From e0cb63682b4478e84e043bd27aadd87da1563343 Mon Sep 17 00:00:00 2001
From: Evgeny Leviant <eleviant at accesssoftek.com>
Date: Tue, 5 May 2026 19:29:08 +0200
Subject: [PATCH 4/4] Add test case
---
.../test/CodeGen/MSKernel/offsetof-check.cpp | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 clang/test/CodeGen/MSKernel/offsetof-check.cpp
diff --git a/clang/test/CodeGen/MSKernel/offsetof-check.cpp b/clang/test/CodeGen/MSKernel/offsetof-check.cpp
new file mode 100644
index 0000000000000..e4fce48d4070e
--- /dev/null
+++ b/clang/test/CodeGen/MSKernel/offsetof-check.cpp
@@ -0,0 +1,66 @@
+// Check that MY_OFFSETOF can be statically evaluated and
+// returns the same results as __builtin_offsetof
+// RUN: %clang_cc1 -fms-kernel -fms-extensions -O2 -triple x86_64-windows-msvc -fsyntax-only %s -o /dev/null
+
+typedef unsigned long long ULONG_PTR;
+
+#define OFFSETOF(type, field) (ULONG_PTR)(&((type *)0)->field)
+#define OFFSET_CHECK(type, field) \
+ static_assert(OFFSETOF(type, field) == __builtin_offsetof(type, field))
+
+typedef struct MyStruct1 {
+ char b;
+} MyStruct1;
+
+typedef struct MyStruct2 {
+ char b;
+ long c;
+} MyStruct2;
+
+typedef struct MyStruct3 {
+ char b;
+ long x[10];
+} MyStruct3;
+
+typedef struct MyStruct4 {
+ char c[10][10];
+ alignas(16) int v;
+} MyStruct4;
+
+typedef struct MyStruct5 {
+ char b;
+ struct X {
+ long m0[10], m1;
+ } y;
+ long c;
+} MyStruct5;
+
+typedef struct MyStruct6 {
+ char b;
+ struct X {
+ long m0[10], m1;
+ } x[10];
+ long c;
+} MyStruct6;
+
+OFFSET_CHECK(MyStruct1, b);
+OFFSET_CHECK(MyStruct2, b);
+OFFSET_CHECK(MyStruct2, c);
+OFFSET_CHECK(MyStruct3, b);
+OFFSET_CHECK(MyStruct3, x[1]);
+OFFSET_CHECK(MyStruct3, x[2]);
+OFFSET_CHECK(MyStruct3, x[3]);
+OFFSET_CHECK(MyStruct4, c[1][1]);
+OFFSET_CHECK(MyStruct4, v);
+OFFSET_CHECK(MyStruct5, b);
+OFFSET_CHECK(MyStruct5, y);
+OFFSET_CHECK(MyStruct5, y.m0[3]);
+OFFSET_CHECK(MyStruct5, y.m1);
+OFFSET_CHECK(MyStruct5, c);
+OFFSET_CHECK(MyStruct6, b);
+OFFSET_CHECK(MyStruct6, x);
+OFFSET_CHECK(MyStruct6, x[1]);
+OFFSET_CHECK(MyStruct6, x[3].m0[3]);
+OFFSET_CHECK(MyStruct6, x[4].m1);
+OFFSET_CHECK(MyStruct6, c);
+
More information about the cfe-commits
mailing list