Skip to content

Commit 56d5049

Browse files
committed
[BoundsSafety][WORK-IN-PROGRESS] Make 'counted_by' work for pointer fields; late parsing for 'counted_by' on decl attr position
This work in based on a patch originally written by Yeoul Na. rdar://125400257
1 parent 24ea6b9 commit 56d5049

File tree

12 files changed

+243
-22
lines changed

12 files changed

+243
-22
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2228,7 +2228,8 @@ def TypeNullUnspecified : TypeAttr {
22282228
def CountedBy : DeclOrTypeAttr {
22292229
let Spellings = [Clang<"counted_by">];
22302230
let Subjects = SubjectList<[Field], ErrorDiag>;
2231-
let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel">];
2231+
let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>];
2232+
let LateParsed = LateAttrParseExperimentalExt;
22322233
let ParseArgumentsAsUnevaluated = 1;
22332234
let Documentation = [CountedByDocs];
22342235
let LangOpts = [COnly];

clang/include/clang/Parse/Parser.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1616,6 +1616,10 @@ class Parser : public CodeCompletionHandler {
16161616
bool EnterScope, bool OnDefinition);
16171617
void ParseLexedAttribute(LateParsedAttribute &LA,
16181618
bool EnterScope, bool OnDefinition);
1619+
void ParseLexedCAttributeList(LateParsedAttrList &LA, bool EnterScope,
1620+
ParsedAttributes *OutAttrs = nullptr);
1621+
void ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
1622+
ParsedAttributes *OutAttrs = nullptr);
16191623
void ParseLexedMethodDeclarations(ParsingClass &Class);
16201624
void ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM);
16211625
void ParseLexedMethodDefs(ParsingClass &Class);
@@ -2503,7 +2507,9 @@ class Parser : public CodeCompletionHandler {
25032507

25042508
void ParseStructDeclaration(
25052509
ParsingDeclSpec &DS,
2506-
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback);
2510+
llvm::function_ref<void(ParsingFieldDeclarator &, Decl *&)>
2511+
FieldsCallback,
2512+
LateParsedAttrList *LateFieldAttrs = nullptr);
25072513

25082514
DeclGroupPtrTy ParseTopLevelStmtDecl();
25092515

@@ -3080,6 +3086,9 @@ class Parser : public CodeCompletionHandler {
30803086
SourceLocation ScopeLoc,
30813087
ParsedAttr::Form Form);
30823088

3089+
void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
3090+
LateParsedAttrList *LateAttrs);
3091+
30833092
void ParseBoundsAttribute(IdentifierInfo &AttrName,
30843093
SourceLocation AttrNameLoc, ParsedAttributes &Attrs,
30853094
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11584,7 +11584,7 @@ class Sema final : public SemaBase {
1158411584
QualType BuildMatrixType(QualType T, Expr *NumRows, Expr *NumColumns,
1158511585
SourceLocation AttrLoc);
1158611586

11587-
QualType BuildCountAttributedArrayType(QualType WrappedTy, Expr *CountExpr);
11587+
QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy, Expr *CountExpr);
1158811588

1158911589
QualType BuildAddressSpaceAttr(QualType &T, LangAS ASIdx, Expr *AddrSpace,
1159011590
SourceLocation AttrLoc);

clang/lib/Parse/ParseDecl.cpp

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3285,6 +3285,17 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
32853285
}
32863286
}
32873287

3288+
void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl,
3289+
LateParsedAttrList *LateAttrs) {
3290+
if (!LateAttrs)
3291+
return;
3292+
3293+
for (auto *LateAttr : *LateAttrs) {
3294+
if (LateAttr->Decls.empty())
3295+
LateAttr->addDecl(Dcl);
3296+
}
3297+
}
3298+
32883299
/// Bounds attributes (e.g., counted_by):
32893300
/// AttrName '(' expression ')'
32903301
void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
@@ -4818,13 +4829,14 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS,
48184829
///
48194830
void Parser::ParseStructDeclaration(
48204831
ParsingDeclSpec &DS,
4821-
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback) {
4832+
llvm::function_ref<void(ParsingFieldDeclarator &, Decl *&)> FieldsCallback,
4833+
LateParsedAttrList *LateFieldAttrs) {
48224834

48234835
if (Tok.is(tok::kw___extension__)) {
48244836
// __extension__ silences extension warnings in the subexpression.
48254837
ExtensionRAIIObject O(Diags); // Use RAII to do this.
48264838
ConsumeToken();
4827-
return ParseStructDeclaration(DS, FieldsCallback);
4839+
return ParseStructDeclaration(DS, FieldsCallback, LateFieldAttrs);
48284840
}
48294841

48304842
// Parse leading attributes.
@@ -4889,10 +4901,12 @@ void Parser::ParseStructDeclaration(
48894901
}
48904902

48914903
// If attributes exist after the declarator, parse them.
4892-
MaybeParseGNUAttributes(DeclaratorInfo.D);
4904+
MaybeParseGNUAttributes(DeclaratorInfo.D, LateFieldAttrs);
48934905

48944906
// We're done with this declarator; invoke the callback.
4895-
FieldsCallback(DeclaratorInfo);
4907+
Decl *Field = nullptr;
4908+
FieldsCallback(DeclaratorInfo, Field);
4909+
DistributeCLateParsedAttrs(DeclaratorInfo.D, Field, LateFieldAttrs);
48964910

48974911
// If we don't have a comma, it is either the end of the list (a ';')
48984912
// or an error, bail out.
@@ -4903,6 +4917,79 @@ void Parser::ParseStructDeclaration(
49034917
}
49044918
}
49054919

4920+
// Parse all attributes in LA, and attach them to Decl D.
4921+
void Parser::ParseLexedCAttributeList(LateParsedAttrList &LA, bool EnterScope, ParsedAttributes *OutAttrs) {
4922+
assert(LA.parseSoon() &&
4923+
"Attribute list should be marked for immediate parsing.");
4924+
for (unsigned i = 0, ni = LA.size(); i < ni; ++i) {
4925+
ParseLexedCAttribute(*LA[i], EnterScope, OutAttrs);
4926+
delete LA[i];
4927+
}
4928+
LA.clear();
4929+
}
4930+
4931+
/// Finish parsing an attribute for which parsing was delayed.
4932+
/// This will be called at the end of parsing a class declaration
4933+
/// for each LateParsedAttribute. We consume the saved tokens and
4934+
/// create an attribute with the arguments filled in. We add this
4935+
/// to the Attribute list for the decl.
4936+
void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, ParsedAttributes *OutAttrs) {
4937+
// Create a fake EOF so that attribute parsing won't go off the end of the
4938+
// attribute.
4939+
Token AttrEnd;
4940+
AttrEnd.startToken();
4941+
AttrEnd.setKind(tok::eof);
4942+
AttrEnd.setLocation(Tok.getLocation());
4943+
AttrEnd.setEofData(LA.Toks.data());
4944+
LA.Toks.push_back(AttrEnd);
4945+
4946+
// Append the current token at the end of the new token stream so that it
4947+
// doesn't get lost.
4948+
LA.Toks.push_back(Tok);
4949+
PP.EnterTokenStream(LA.Toks, true, /*IsReinject=*/true);
4950+
// Consume the previously pushed token.
4951+
ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
4952+
4953+
ParsedAttributes Attrs(AttrFactory);
4954+
4955+
assert(
4956+
LA.Decls.size() < 2 &&
4957+
"late field attribute expects to have at most a single declaration.");
4958+
4959+
Decl *D = LA.Decls.empty() ? nullptr : LA.Decls[0];
4960+
4961+
// If the Decl is on a function, add function parameters to the scope.
4962+
{
4963+
std::unique_ptr<ParseScope> Scope;
4964+
EnterScope &= D && D->isFunctionOrFunctionTemplate();
4965+
if (EnterScope) {
4966+
Scope.reset(new ParseScope(this, Scope::FnScope | Scope::DeclScope));
4967+
Actions.ActOnReenterFunctionContext(Actions.CurScope, D);
4968+
}
4969+
ParseBoundsAttribute(LA.AttrName, LA.AttrNameLoc, Attrs,
4970+
/*ScopeName*/nullptr, SourceLocation(),
4971+
ParsedAttr::Form::GNU());
4972+
if (EnterScope) {
4973+
Actions.ActOnExitFunctionContext();
4974+
}
4975+
}
4976+
4977+
for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i)
4978+
Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs);
4979+
4980+
// Due to a parsing error, we either went over the cached tokens or
4981+
// there are still cached tokens left, so we skip the leftover tokens.
4982+
while (Tok.isNot(tok::eof))
4983+
ConsumeAnyToken();
4984+
4985+
if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData())
4986+
ConsumeAnyToken();
4987+
4988+
if (OutAttrs) {
4989+
OutAttrs->takeAllFrom(Attrs);
4990+
}
4991+
}
4992+
49064993
/// ParseStructUnionBody
49074994
/// struct-contents:
49084995
/// struct-declaration-list
@@ -4926,6 +5013,11 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
49265013
ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope);
49275014
Actions.ActOnTagStartDefinition(getCurScope(), TagDecl);
49285015

5016+
// `LateAttrParseExperimentalExtOnly=true` requests that only attributes
5017+
// marked with `LateAttrParseExperimentalExt` are late parsed.
5018+
LateParsedAttrList LateFieldAttrs(/*PSoon=*/false,
5019+
/*LateAttrParseExperimentalExtOnly=*/true);
5020+
49295021
// While we still have something to read, read the declarations in the struct.
49305022
while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
49315023
Tok.isNot(tok::eof)) {
@@ -4976,18 +5068,19 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
49765068
}
49775069

49785070
if (!Tok.is(tok::at)) {
4979-
auto CFieldCallback = [&](ParsingFieldDeclarator &FD) {
5071+
auto CFieldCallback = [&](ParsingFieldDeclarator &FD, Decl *&Dcl) {
49805072
// Install the declarator into the current TagDecl.
49815073
Decl *Field =
49825074
Actions.ActOnField(getCurScope(), TagDecl,
49835075
FD.D.getDeclSpec().getSourceRange().getBegin(),
49845076
FD.D, FD.BitfieldSize);
49855077
FD.complete(Field);
5078+
Dcl = Field;
49865079
};
49875080

49885081
// Parse all the comma separated declarators.
49895082
ParsingDeclSpec DS(*this);
4990-
ParseStructDeclaration(DS, CFieldCallback);
5083+
ParseStructDeclaration(DS, CFieldCallback, &LateFieldAttrs);
49915084
} else { // Handle @defs
49925085
ConsumeToken();
49935086
if (!Tok.isObjCAtKeyword(tok::objc_defs)) {
@@ -5028,7 +5121,11 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
50285121

50295122
ParsedAttributes attrs(AttrFactory);
50305123
// If attributes exist after struct contents, parse them.
5031-
MaybeParseGNUAttributes(attrs);
5124+
MaybeParseGNUAttributes(attrs, &LateFieldAttrs);
5125+
5126+
assert(!getLangOpts().CPlusPlus);
5127+
for (auto *LateAttr : LateFieldAttrs)
5128+
ParseLexedCAttribute(*LateAttr, true);
50325129

50335130
SmallVector<Decl *, 32> FieldDecls(TagDecl->fields());
50345131

clang/lib/Parse/ParseObjc.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
778778
}
779779

780780
bool addedToDeclSpec = false;
781-
auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) {
781+
auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD, Decl *&Dcl) {
782782
if (FD.D.getIdentifier() == nullptr) {
783783
Diag(AtLoc, diag::err_objc_property_requires_field_name)
784784
<< FD.D.getSourceRange();
@@ -816,6 +816,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
816816
MethodImplKind);
817817

818818
FD.complete(Property);
819+
Dcl = Property;
819820
};
820821

821822
// Parse all the comma separated declarators.
@@ -2024,7 +2025,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl,
20242025
continue;
20252026
}
20262027

2027-
auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
2028+
auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD, Decl *&Dcl) {
20282029
assert(getObjCDeclContext() == interfaceDecl &&
20292030
"Ivar should have interfaceDecl as its decl context");
20302031
// Install the declarator into the interface decl.
@@ -2035,6 +2036,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl,
20352036
if (Field)
20362037
AllIvarDecls.push_back(Field);
20372038
FD.complete(Field);
2039+
Dcl = Field;
20382040
};
20392041

20402042
// Parse all the comma separated declarators.

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8548,8 +8548,8 @@ static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) {
85488548
}
85498549

85508550
static bool
8551-
CheckCountExpr(Sema &S, FieldDecl *FD, Expr *E,
8552-
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) {
8551+
CheckCountExprOnField(Sema &S, FieldDecl *FD, Expr *E,
8552+
llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) {
85538553
if (FD->getParent()->isUnion()) {
85548554
S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union)
85558555
<< FD->getSourceRange();
@@ -8565,7 +8565,8 @@ CheckCountExpr(Sema &S, FieldDecl *FD, Expr *E,
85658565
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
85668566
LangOptions::StrictFlexArraysLevelKind::IncompleteOnly;
85678567

8568-
if (!Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FD->getType(),
8568+
if (FD->getType()->isArrayType() &&
8569+
!Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FD->getType(),
85698570
StrictFlexArraysLevel, true)) {
85708571
// The "counted_by" attribute must be on a flexible array member.
85718572
SourceRange SR = FD->getLocation();
@@ -8634,10 +8635,10 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
86348635
return;
86358636

86368637
llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
8637-
if (CheckCountExpr(S, FD, CountExpr, Decls))
8638+
if (CheckCountExprOnField(S, FD, CountExpr, Decls))
86388639
return;
86398640

8640-
QualType CAT = S.BuildCountAttributedArrayType(FD->getType(), CountExpr);
8641+
QualType CAT = S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr);
86418642
FD->setType(CAT);
86428643
}
86438644

clang/lib/Sema/SemaType.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9787,9 +9787,9 @@ BuildTypeCoupledDecls(Expr *E,
97879787
Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref*/ false));
97889788
}
97899789

9790-
QualType Sema::BuildCountAttributedArrayType(QualType WrappedTy,
9791-
Expr *CountExpr) {
9792-
assert(WrappedTy->isIncompleteArrayType());
9790+
QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
9791+
Expr *CountExpr) {
9792+
assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType());
97939793

97949794
llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls;
97959795
BuildTypeCoupledDecls(CountExpr, Decls);

clang/lib/Sema/TreeTransform.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7321,7 +7321,7 @@ QualType TreeTransform<Derived>::TransformCountAttributedType(
73217321
if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() ||
73227322
OldCount != NewCount) {
73237323
// Currently, CountAttributedType can only wrap incomplete array types.
7324-
Result = SemaRef.BuildCountAttributedArrayType(InnerTy, NewCount);
7324+
Result = SemaRef.BuildCountAttributedArrayOrPointerType(InnerTy, NewCount);
73257325
}
73267326

73277327
TLB.push<CountAttributedTypeLoc>(Result);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// RUN: %clang_cc1 -fexperimental-late-parse-attributes %s -ast-dump | FileCheck %s
2+
3+
#define __counted_by(f) __attribute__((counted_by(f)))
4+
5+
struct size_known;
6+
7+
struct at_decl {
8+
struct size_known *buf __counted_by(count);
9+
int count;
10+
};
11+
// CHECK-LABEL: struct at_decl definition
12+
// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by(count)':'struct size_known *'
13+
// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int'
14+
15+
struct at_decl_anon_count {
16+
struct size_known *buf __counted_by(count);
17+
struct {
18+
int count;
19+
};
20+
};
21+
22+
// CHECK-LABEL: struct at_decl_anon_count definition
23+
// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by(count)':'struct size_known *'
24+
// CHECK-NEXT: |-RecordDecl {{.*}} struct definition
25+
// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int'
26+
// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct at_decl_anon_count::(anonymous at {{.*}})'
27+
// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int'
28+
// CHECK-NEXT: |-Field {{.*}} '' 'struct at_decl_anon_count::(anonymous at {{.*}})'
29+
// CHECK-NEXT: `-Field {{.*}} 'count' 'int'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %clang_cc1 -DNEEDS_LATE_PARSING -fno-experimental-late-parse-attributes -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -DNEEDS_LATE_PARSING -fsyntax-only -verify %s
3+
4+
// RUN: %clang_cc1 -UNEEDS_LATE_PARSING -fno-experimental-late-parse-attributes -fsyntax-only -verify=ok %s
5+
// RUN: %clang_cc1 -UNEEDS_LATE_PARSING -fsyntax-only -verify=ok %s
6+
7+
#define __counted_by(f) __attribute__((counted_by(f)))
8+
9+
struct size_known { int dummy; };
10+
11+
#ifdef NEEDS_LATE_PARSING
12+
struct at_decl {
13+
// expected-error@+1{{use of undeclared identifier 'count'}}
14+
struct size_known *buf __counted_by(count);
15+
int count;
16+
};
17+
18+
#else
19+
20+
// ok-no-diagnostics
21+
struct at_decl {
22+
int count;
23+
struct size_known *buf __counted_by(count);
24+
};
25+
26+
#endif

0 commit comments

Comments
 (0)