[clang] [Sema] Call ActOnFields before late parsing in ParseStructUnionBody (PR #187166)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 17 17:51:54 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Pengxiang Huang (Pengxiang-Huang)
<details>
<summary>Changes</summary>
Implements for: #<!-- -->186914
Move the call to `ActOnFields()` before `ParseLexedCAttributeList()` in ParseStructUnionBody for reordering so that the struct type is complete when late-parsed attributes like counted_by get evaluated. This is a prerequisite for supporting sizeof/offsetof expressions in counted_by evaluation.
Update the heuristic for `GetEnclosingNamedOrTopAnonRecord`. Remove the `isCompleteDefinition()` condition since it will always return true under the new ordering. The `GetEnclosingNamedOrTopAnonRecord` intend to treat the unnamed and anonymous struct permissively.
Add one test to verify the new ordering still make sure the function of unnamed and anonymous struct works normally.
---
Full diff: https://github.com/llvm/llvm-project/pull/187166.diff
3 Files Affected:
- (modified) clang/lib/Parse/ParseDecl.cpp (+3-3)
- (modified) clang/lib/Sema/SemaBoundsSafety.cpp (+1-2)
- (added) clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs-anon.c (+65)
``````````diff
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 72935f427b7f8..fe281f156e988 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4989,13 +4989,13 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
// If attributes exist after struct contents, parse them.
MaybeParseGNUAttributes(attrs, &LateFieldAttrs);
- // Late parse field attributes if necessary.
- ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false);
-
SmallVector<Decl *, 32> FieldDecls(TagDecl->fields());
Actions.ActOnFields(getCurScope(), RecordLoc, TagDecl, FieldDecls,
T.getOpenLocation(), T.getCloseLocation(), attrs);
+
+ // Late parse field attributes if necessary.
+ ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false);
StructScope.Exit();
Actions.ActOnTagFinishDefinition(getCurScope(), TagDecl, T.getRange());
}
diff --git a/clang/lib/Sema/SemaBoundsSafety.cpp b/clang/lib/Sema/SemaBoundsSafety.cpp
index 8c8e37d321938..faf352b3a366f 100644
--- a/clang/lib/Sema/SemaBoundsSafety.cpp
+++ b/clang/lib/Sema/SemaBoundsSafety.cpp
@@ -32,8 +32,7 @@ static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {
// However, the struct may not be fully processed yet to determine
// whether it's anonymous or not. In that case, this function treats it as
// an anonymous struct and tries to find a named parent.
- while (RD && (RD->isAnonymousStructOrUnion() ||
- (!RD->isCompleteDefinition() && RD->getName().empty()))) {
+ while (RD && (RD->isAnonymousStructOrUnion() || RD->getName().empty())) {
const auto *Parent = dyn_cast<RecordDecl>(RD->getParent());
if (!Parent)
break;
diff --git a/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs-anon.c b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs-anon.c
new file mode 100644
index 0000000000000..8b74f46b064f5
--- /dev/null
+++ b/clang/test/Sema/attr-counted-by-late-parsed-struct-ptrs-anon.c
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -fexperimental-late-parse-attributes -fsyntax-only -verify %s
+
+// Test that counted_by works correctly with late parsing when ActOnFields
+// is called before late-parsed attributes are evaluated. This allows offset
+// expressions in counted_by to be evaluated.
+
+#define __counted_by(f) __attribute__((counted_by(f)))
+
+struct size_known {
+ int field;
+};
+
+//==============================================================================
+// Verify anonymous struct handling works corrctly under currect ordering.
+// GetEnclosingNamedOrTopAnonRecord must correctly walk through anonymous
+// structs when the struct is already marked complete.
+//==============================================================================
+
+// count in outer struct, buf in anonymous struct
+struct on_pointer_anon_buf {
+ int count;
+ struct {
+ struct size_known *buf __counted_by(count);
+ };
+};
+
+// both count and buf in anonymous struct
+struct on_pointer_anon_both {
+ struct {
+ int count;
+ struct size_known *buf __counted_by(count);
+ };
+};
+
+// nested anonymous structs
+struct on_pointer_nested_anon {
+ int count;
+ struct {
+ struct {
+ struct size_known *buf __counted_by(count);
+ };
+ };
+};
+
+//==============================================================================
+// Verify non-anonymous unnamed structs correctly reject counted_by if it
+// reference fields in the outer struct.
+//==============================================================================
+
+// count in outer, buf in non-anonymous unnamed struct — should reject
+struct on_pointer_named_inner {
+ int count; // expected-note{{'count' declared here}}
+ struct {
+ // expected-error at +1{{'counted_by' field 'count' isn't within the same struct as the annotated pointer}}
+ struct size_known *buf __counted_by(count);
+ } inner;
+};
+
+// both in non-anonymous unnamed struct — should accept
+struct on_pointer_named_inner_both {
+ struct {
+ int count;
+ struct size_known *buf __counted_by(count);
+ } inner;
+};
``````````
</details>
https://github.com/llvm/llvm-project/pull/187166
More information about the cfe-commits
mailing list