Skip to content

Commit f0ec140

Browse files
committed
[BoundsSafety] Support late parsing for counted_by in type positions
Previously, late parsing only supported attributes in declaration position and could not resolve references to member declarations that appeared later in the struct. Additionally, the compiler incorrectly accepted `counted_by` on nested pointer types, which should be rejected. Instead, these attributes were incorrectly attached to the outermost pointer type because they were being treated as GNU-style declaration attributes rather than type attributes. This commit adds a vector of `LateParsedAttribute *` to `DeclaratorChunk`, similar to how `ParsedAttr` is attached to `DeclaratorChunk`. Since DeclSpec doesn't see the definition of `LateParsedAttribute`, it is treated as an opaque pointer type. When the late-attribute is actually parsed, it is now correctly attached to the appropriate nested type level, enabling references to members declared later when the late-parsed attribute is in type position, and rejection of invalid nested pointer cases. Issue #166411
1 parent 4cdeb7d commit f0ec140

23 files changed

+368
-361
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7012,6 +7012,9 @@ def err_builtin_counted_by_ref_invalid_use : Error<
70127012
"value returned by '__builtin_counted_by_ref' cannot be used in "
70137013
"%select{an array subscript|a binary}0 expression">;
70147014

7015+
def err_counted_by_on_nested_pointer : Error<
7016+
"'%select{counted_by|sized_by|counted_by_or_null|sized_by_or_null}0' attribute on nested pointer type is not allowed">;
7017+
70157018
let CategoryName = "ARC Semantic Issue" in {
70167019

70177020
// ARC-mode diagnostics.

clang/include/clang/Parse/Parser.h

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,10 +1161,11 @@ class Parser : public CodeCompletionHandler {
11611161
IdentifierInfo *MacroII = nullptr;
11621162
SourceLocation AttrNameLoc;
11631163
SmallVector<Decl *, 2> Decls;
1164+
unsigned NestedTypeLevel;
11641165

11651166
explicit LateParsedAttribute(Parser *P, IdentifierInfo &Name,
1166-
SourceLocation Loc)
1167-
: Self(P), AttrName(Name), AttrNameLoc(Loc) {}
1167+
SourceLocation Loc, unsigned Level = 0)
1168+
: Self(P), AttrName(Name), AttrNameLoc(Loc), NestedTypeLevel(Level) {}
11681169

11691170
void ParseLexedAttributes() override;
11701171

@@ -1889,10 +1890,12 @@ class Parser : public CodeCompletionHandler {
18891890
DeclSpec &DS, AccessSpecifier AS, DeclSpecContext DSContext,
18901891
LateParsedAttrList *LateAttrs = nullptr);
18911892

1892-
void ParseSpecifierQualifierList(
1893-
DeclSpec &DS, AccessSpecifier AS = AS_none,
1894-
DeclSpecContext DSC = DeclSpecContext::DSC_normal) {
1895-
ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC);
1893+
void
1894+
ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS = AS_none,
1895+
DeclSpecContext DSC = DeclSpecContext::DSC_normal,
1896+
LateParsedAttrList *LateAttrs = nullptr) {
1897+
ParseSpecifierQualifierList(DS, getImplicitTypenameContext(DSC), AS, DSC,
1898+
LateAttrs);
18961899
}
18971900

18981901
/// ParseSpecifierQualifierList
@@ -1903,10 +1906,12 @@ class Parser : public CodeCompletionHandler {
19031906
/// [GNU] attributes specifier-qualifier-list[opt]
19041907
/// \endverbatim
19051908
///
1906-
void ParseSpecifierQualifierList(
1907-
DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
1908-
AccessSpecifier AS = AS_none,
1909-
DeclSpecContext DSC = DeclSpecContext::DSC_normal);
1909+
void
1910+
ParseSpecifierQualifierList(DeclSpec &DS,
1911+
ImplicitTypenameContext AllowImplicitTypename,
1912+
AccessSpecifier AS = AS_none,
1913+
DeclSpecContext DSC = DeclSpecContext::DSC_normal,
1914+
LateParsedAttrList *LateAttrs = nullptr);
19101915

19111916
/// ParseEnumSpecifier
19121917
/// \verbatim
@@ -2444,7 +2449,8 @@ class Parser : public CodeCompletionHandler {
24442449
SourceLocation ScopeLoc,
24452450
ParsedAttr::Form Form);
24462451

2447-
void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs);
2452+
void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
2453+
LateParsedAttrList *LateAttrs);
24482454

24492455
/// Bounds attributes (e.g., counted_by):
24502456
/// \verbatim
@@ -2610,7 +2616,8 @@ class Parser : public CodeCompletionHandler {
26102616
void ParseTypeQualifierListOpt(
26112617
DeclSpec &DS, unsigned AttrReqs = AR_AllAttributesParsed,
26122618
bool AtomicOrPtrauthAllowed = true, bool IdentifierRequired = false,
2613-
llvm::function_ref<void()> CodeCompletionHandler = {});
2619+
llvm::function_ref<void()> CodeCompletionHandler = {},
2620+
LateParsedAttrList *LateAttrs = nullptr);
26142621

26152622
/// ParseDirectDeclarator
26162623
/// \verbatim

clang/include/clang/Sema/DeclSpec.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1238,6 +1238,9 @@ struct DeclaratorChunk {
12381238

12391239
ParsedAttributesView AttrList;
12401240

1241+
using LateAttrListTy = SmallVector<void *, 1>;
1242+
LateAttrListTy LateAttrList;
1243+
12411244
struct PointerTypeInfo {
12421245
/// The type qualifiers: const/volatile/restrict/unaligned/atomic.
12431246
LLVM_PREFERRED_TYPE(DeclSpec::TQ)
@@ -2325,13 +2328,16 @@ class Declarator {
23252328
/// This function takes attrs by R-Value reference because it takes ownership
23262329
/// of those attributes from the parameter.
23272330
void AddTypeInfo(const DeclaratorChunk &TI, ParsedAttributes &&attrs,
2328-
SourceLocation EndLoc) {
2331+
SourceLocation EndLoc,
2332+
const DeclaratorChunk::LateAttrListTy &LateAttrs = {}) {
23292333
DeclTypeInfo.push_back(TI);
23302334
DeclTypeInfo.back().getAttrs().prepend(attrs.begin(), attrs.end());
23312335
getAttributePool().takeAllFrom(attrs.getPool());
23322336

23332337
if (!EndLoc.isInvalid())
23342338
SetRangeEnd(EndLoc);
2339+
2340+
DeclTypeInfo.back().LateAttrList.assign(LateAttrs);
23352341
}
23362342

23372343
/// AddTypeInfo - Add a chunk to this declarator. Also extend the range to

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,8 +2457,8 @@ class Sema final : public SemaBase {
24572457
/// `counted_by_or_null` attribute.
24582458
///
24592459
/// \returns false iff semantically valid.
2460-
bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
2461-
bool OrNull);
2460+
bool CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, unsigned Level,
2461+
bool CountInBytes, bool OrNull);
24622462

24632463
/// Perform Bounds Safety Semantic checks for assigning to a `__counted_by` or
24642464
/// `__counted_by_or_null` pointer type \param LHSTy.
@@ -4198,7 +4198,8 @@ class Sema final : public SemaBase {
41984198

41994199
/// ActOnFinishDelayedAttribute - Invoked when we have finished parsing an
42004200
/// attribute for which parsing is delayed.
4201-
void ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs);
4201+
void ActOnFinishDelayedAttribute(Scope *S, Decl *D, ParsedAttributes &Attrs,
4202+
unsigned NestedTypeLevel = 0);
42024203

42034204
/// Diagnose any unused parameters in the given sequence of
42044205
/// ParmVarDecl pointers.
@@ -5071,7 +5072,8 @@ class Sema final : public SemaBase {
50715072
void ProcessDeclAttributeList(Scope *S, Decl *D,
50725073
const ParsedAttributesView &AttrList,
50735074
const ProcessDeclAttributeOptions &Options =
5074-
ProcessDeclAttributeOptions());
5075+
ProcessDeclAttributeOptions(),
5076+
unsigned NestedTypeLevel = 0);
50755077

50765078
/// Annotation attributes are the only attributes allowed after an access
50775079
/// specifier.

clang/lib/Parse/ParseDecl.cpp

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,7 +1943,11 @@ Parser::DeclGroupPtrTy Parser::ParseSimpleDeclaration(
19431943

19441944
ParsedTemplateInfo TemplateInfo;
19451945
DeclSpecContext DSContext = getDeclSpecContextFromDeclaratorContext(Context);
1946-
ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext);
1946+
// FIXME: Why is PSoon true?
1947+
LateParsedAttrList BoundsSafetyLateAttrs(
1948+
/*PSoon=*/true, /*LateAttrParseExperimentalExtOnly=*/true);
1949+
ParseDeclarationSpecifiers(DS, TemplateInfo, AS_none, DSContext,
1950+
&BoundsSafetyLateAttrs);
19471951

19481952
// If we had a free-standing type definition with a missing semicolon, we
19491953
// may get this far before the problem becomes obvious.
@@ -2725,12 +2729,12 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(
27252729

27262730
void Parser::ParseSpecifierQualifierList(
27272731
DeclSpec &DS, ImplicitTypenameContext AllowImplicitTypename,
2728-
AccessSpecifier AS, DeclSpecContext DSC) {
2732+
AccessSpecifier AS, DeclSpecContext DSC, LateParsedAttrList *LateAttrs) {
27292733
ParsedTemplateInfo TemplateInfo;
27302734
/// specifier-qualifier-list is a subset of declaration-specifiers. Just
27312735
/// parse declaration-specifiers and complain about extra stuff.
27322736
/// TODO: diagnose attribute-specifiers and alignment-specifiers.
2733-
ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, nullptr,
2737+
ParseDeclarationSpecifiers(DS, TemplateInfo, AS, DSC, LateAttrs,
27342738
AllowImplicitTypename);
27352739

27362740
// Validate declspec for type-name.
@@ -3136,15 +3140,37 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
31363140
}
31373141
}
31383142

3139-
void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
3143+
void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
31403144
LateParsedAttrList *LateAttrs) {
31413145
if (!LateAttrs)
31423146
return;
31433147

3148+
unsigned NestedLevel = 0;
3149+
for (unsigned i = 0; i < D.getNumTypeObjects(); ++i) {
3150+
DeclaratorChunk &DC = D.getTypeObject(i);
3151+
3152+
switch (DC.Kind) {
3153+
case DeclaratorChunk::Pointer:
3154+
case DeclaratorChunk::Array:
3155+
break;
3156+
default:
3157+
continue;
3158+
}
3159+
3160+
// Extract `LateParsedAttribute *` from `DeclaratorChunk`.
3161+
for (auto *OpaqueLA : DC.LateAttrList) {
3162+
auto *LA = static_cast<LateParsedAttribute *>(OpaqueLA);
3163+
LA->NestedTypeLevel = NestedLevel;
3164+
LateAttrs->push_back(LA);
3165+
}
3166+
NestedLevel++;
3167+
}
3168+
3169+
// Attach `Decl *` to each `LateParsedAttribute *`.
31443170
if (Dcl) {
3145-
for (auto *LateAttr : *LateAttrs) {
3146-
if (LateAttr->Decls.empty())
3147-
LateAttr->addDecl(Dcl);
3171+
for (auto *LA : *LateAttrs) {
3172+
if (LA->Decls.empty())
3173+
LA->addDecl(Dcl);
31483174
}
31493175
}
31503176
}
@@ -3217,12 +3243,6 @@ void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
32173243
ArgExprs.push_back(ArgExpr.get());
32183244
Parens.consumeClose();
32193245

3220-
ASTContext &Ctx = Actions.getASTContext();
3221-
3222-
ArgExprs.push_back(IntegerLiteral::Create(
3223-
Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.getSizeType()), 0),
3224-
Ctx.getSizeType(), SourceLocation()));
3225-
32263246
Attrs.addNew(&AttrName, SourceRange(AttrNameLoc, Parens.getCloseLocation()),
32273247
AttributeScopeInfo(), ArgExprs.data(), ArgExprs.size(), Form);
32283248
}
@@ -4706,7 +4726,8 @@ void Parser::ParseStructDeclaration(
47064726
MaybeParseCXX11Attributes(Attrs);
47074727

47084728
// Parse the common specifier-qualifiers-list piece.
4709-
ParseSpecifierQualifierList(DS);
4729+
ParseSpecifierQualifierList(DS, AS_none, DeclSpecContext::DSC_normal,
4730+
LateFieldAttrs);
47104731

47114732
// If there are no declarators, this is a free-standing declaration
47124733
// specifier. Let the actions module cope with it.
@@ -4768,7 +4789,7 @@ void Parser::ParseStructDeclaration(
47684789
// We're done with this declarator; invoke the callback.
47694790
Decl *Field = FieldsCallback(DeclaratorInfo);
47704791
if (Field)
4771-
DistributeCLateParsedAttrs(Field, LateFieldAttrs);
4792+
DistributeCLateParsedAttrs(DeclaratorInfo.D, Field, LateFieldAttrs);
47724793

47734794
// If we don't have a comma, it is either the end of the list (a ';')
47744795
// or an error, bail out.
@@ -4825,7 +4846,8 @@ void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
48254846
SourceLocation(), ParsedAttr::Form::GNU(), nullptr);
48264847

48274848
for (auto *D : LA.Decls)
4828-
Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs);
4849+
Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs,
4850+
LA.NestedTypeLevel);
48294851

48304852
// Due to a parsing error, we either went over the cached tokens or
48314853
// there are still cached tokens left, so we skip the leftover tokens.
@@ -6124,7 +6146,8 @@ bool Parser::isConstructorDeclarator(bool IsUnqualified, bool DeductionGuide,
61246146

61256147
void Parser::ParseTypeQualifierListOpt(
61266148
DeclSpec &DS, unsigned AttrReqs, bool AtomicOrPtrauthAllowed,
6127-
bool IdentifierRequired, llvm::function_ref<void()> CodeCompletionHandler) {
6149+
bool IdentifierRequired, llvm::function_ref<void()> CodeCompletionHandler,
6150+
LateParsedAttrList *LateAttrs) {
61286151
if ((AttrReqs & AR_CXX11AttributesParsed) &&
61296152
isAllowedCXX11AttributeSpecifier()) {
61306153
ParsedAttributes Attrs(AttrFactory);
@@ -6266,7 +6289,9 @@ void Parser::ParseTypeQualifierListOpt(
62666289
// recovery is graceful.
62676290
if (AttrReqs & AR_GNUAttributesParsed ||
62686291
AttrReqs & AR_GNUAttributesParsedAndRejected) {
6269-
ParseGNUAttributes(DS.getAttributes());
6292+
6293+
assert(!LateAttrs || LateAttrs->lateAttrParseExperimentalExtOnly());
6294+
ParseGNUAttributes(DS.getAttributes(), LateAttrs);
62706295
continue; // do *not* consume the next token!
62716296
}
62726297
// otherwise, FALL THROUGH!
@@ -6447,21 +6472,33 @@ void Parser::ParseDeclaratorInternal(Declarator &D,
64476472
((D.getContext() != DeclaratorContext::CXXNew)
64486473
? AR_GNUAttributesParsed
64496474
: AR_GNUAttributesParsedAndRejected);
6475+
LateParsedAttrList LateAttrs(/*PSoon=*/true,
6476+
/*LateAttrParseExperimentalExtOnly=*/true);
64506477
ParseTypeQualifierListOpt(DS, Reqs, /*AtomicOrPtrauthAllowed=*/true,
6451-
!D.mayOmitIdentifier());
6478+
!D.mayOmitIdentifier(), {}, &LateAttrs);
64526479
D.ExtendWithDeclSpec(DS);
64536480

64546481
// Recursively parse the declarator.
64556482
Actions.runWithSufficientStackSpace(
64566483
D.getBeginLoc(), [&] { ParseDeclaratorInternal(D, DirectDeclParser); });
6457-
if (Kind == tok::star)
6484+
if (Kind == tok::star) {
6485+
DeclaratorChunk::LateAttrListTy OpaqueLateAttrList;
6486+
if (getLangOpts().ExperimentalLateParseAttributes && !LateAttrs.empty()) {
6487+
if (!D.isFunctionDeclarator()) {
6488+
for (auto LA : LateAttrs) {
6489+
OpaqueLateAttrList.push_back(LA);
6490+
}
6491+
}
6492+
LateAttrs.clear();
6493+
}
64586494
// Remember that we parsed a pointer type, and remember the type-quals.
64596495
D.AddTypeInfo(DeclaratorChunk::getPointer(
64606496
DS.getTypeQualifiers(), Loc, DS.getConstSpecLoc(),
64616497
DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(),
64626498
DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()),
6463-
std::move(DS.getAttributes()), SourceLocation());
6464-
else
6499+
std::move(DS.getAttributes()), SourceLocation(),
6500+
OpaqueLateAttrList);
6501+
} else
64656502
// Remember that we parsed a Block type, and remember the type-quals.
64666503
D.AddTypeInfo(
64676504
DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc),

clang/lib/Sema/SemaBoundsSafety.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ enum class CountedByInvalidPointeeTypeKind {
5050
VALID,
5151
};
5252

53-
bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
54-
bool OrNull) {
53+
bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, unsigned Level,
54+
bool CountInBytes, bool OrNull) {
5555
// Check the context the attribute is used in
5656

5757
unsigned Kind = getCountAttrKind(CountInBytes, OrNull);
@@ -62,6 +62,12 @@ bool Sema::CheckCountedByAttrOnField(FieldDecl *FD, Expr *E, bool CountInBytes,
6262
return true;
6363
}
6464

65+
if (Level != 0) {
66+
Diag(FD->getBeginLoc(), diag::err_counted_by_on_nested_pointer)
67+
<< Kind << FD->getSourceRange();
68+
return true;
69+
}
70+
6571
const auto FieldTy = FD->getType();
6672
if (FieldTy->isArrayType() && (CountInBytes || OrNull)) {
6773
Diag(FD->getBeginLoc(),

clang/lib/Sema/SemaDecl.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16873,11 +16873,13 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
1687316873
/// When we finish delayed parsing of an attribute, we must attach it to the
1687416874
/// relevant Decl.
1687516875
void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D,
16876-
ParsedAttributes &Attrs) {
16876+
ParsedAttributes &Attrs,
16877+
unsigned NestedTypeLevel) {
1687716878
// Always attach attributes to the underlying decl.
1687816879
if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D))
1687916880
D = TD->getTemplatedDecl();
16880-
ProcessDeclAttributeList(S, D, Attrs);
16881+
ProcessDeclAttributeList(S, D, Attrs, ProcessDeclAttributeOptions(),
16882+
NestedTypeLevel);
1688116883
ProcessAPINotes(D);
1688216884

1688316885
if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D))

0 commit comments

Comments
 (0)