Skip to content

Commit 2918973

Browse files
authored
Reland #90786 ([BoundsSafety] Allow 'counted_by' attribute on pointers in structs in C) (#93121)
[BoundsSafety] Reland #93121 Allow 'counted_by' attribute on pointers in structs in C (#93121) Fixes #92687. Previously the attribute was only allowed on flexible array members. This patch patch changes this to also allow the attribute on pointer fields in structs and also allows late parsing of the attribute in some contexts. For example this previously wasn't allowed: ``` struct BufferTypeDeclAttributePosition { size_t count; char* buffer __counted_by(count); // Now allowed } ``` Note the attribute is prevented on pointee types where the size isn't known at compile time. In particular pointee types that are: * Incomplete (e.g. `void`) and sizeless types * Function types (e.g. the pointee of a function pointer) * Struct types with a flexible array member This patch also introduces late parsing of the attribute when used in the declaration attribute position. For example ``` struct BufferTypeDeclAttributePosition { char* buffer __counted_by(count); // Now allowed size_t count; } ``` is now allowed but **only** when passing `-fexperimental-late-parse-attributes`. The motivation for using late parsing here is to avoid breaking the data layout of structs in existing code that want to use the `counted_by` attribute. This patch is the first use of `LateAttrParseExperimentalExt` in `Attr.td` that was introduced in a previous patch. Note by allowing the attribute on struct member pointers this now allows the possiblity of writing the attribute in the type attribute position. For example: ``` struct BufferTypeAttributePosition { size_t count; char *__counted_by(count) buffer; // Now allowed } ``` However, the attribute in this position is still currently parsed immediately rather than late parsed. So this will not parse currently: ``` struct BufferTypeAttributePosition { char *__counted_by(count) buffer; // Fails to parse size_t count; } ``` The intention is to lift this restriction in future patches. It has not been done in this patch to keep this size of this commit small. There are also several other follow up changes that will need to be addressed in future patches: * Make late parsing working with anonymous structs (see `on_pointer_anon_buf` in `attr-counted-by-late-parsed-struct-ptrs.c`). * Allow `counted_by` on more subjects (e.g. parameters, returns types) when `-fbounds-safety` is enabled. * Make use of the attribute on pointer types in code gen (e.g. for `_builtin_dynamic_object_size` and UBSan's array-bounds checks). This work is heavily based on a patch originally written by Yeoul Na. ** Differences between #93121 and this patch ** * The memory leak that caused #93121 to be reverted (see #92687) should now be fixed. See "The Memory Leak". * The fix to `pragma-attribute-supported-attributes-list.test` (originally in cef6387) has been incorporated into this patch. * A relaxation of counted_by semantics (originally in 112eadd) has been incorporated into this patch. * The assert in `Parser::DistributeCLateParsedAttrs` has been removed because that broke downstream code. * The switch statement in `Parser::ParseLexedCAttribute` has been removed in favor of using `Parser::ParseGNUAttributeArgs` which does the same thing but is more feature complete. * The `EnterScope` parameter has been plumbed through `Parser::ParseLexedCAttribute` and `Parser::ParseLexedCAttributeList`. It currently doesn't do anything but it will be needed in future commits. ** The Memory Leak ** The problem was that these lines parsed the attributes but then did nothing to free the memory ``` assert(!getLangOpts().CPlusPlus); for (auto *LateAttr : LateFieldAttrs) ParseLexedCAttribute(*LateAttr); ``` To fix this this a new `Parser::ParseLexedCAttributeList` method has been added (based on `Parser::ParseLexedAttributeList`) which does the necessary memory management. The intention is to merge these two methods together so there is just one implementation in a future patch (#93263). A more principled fixed here would be to fix the ownership of the `LateParsedAttribute` objects. In principle `LateParsedAttrList` should own its pointers exclusively and be responsible for deallocating them. Unfortunately this is complicated by `LateParsedAttribute` objects also being stored in another data structure (`LateParsedDeclarations`) as can be seen below (`LA` gets stored in two places). ``` // Handle attributes with arguments that require late parsing. LateParsedAttribute *LA = new LateParsedAttribute(this, *AttrName, AttrNameLoc); LateAttrs->push_back(LA); // Attributes in a class are parsed at the end of the class, along // with other late-parsed declarations. if (!ClassStack.empty() && !LateAttrs->parseSoon()) getCurrentClass().LateParsedDeclarations.push_back(LA); ``` this means the ownership of LateParsedAttribute objects isn't very clear. rdar://125400257
1 parent 39d32b2 commit 2918973

23 files changed

+1147
-149
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ New Compiler Flags
334334

335335
- ``-fexperimental-late-parse-attributes`` enables an experimental feature to
336336
allow late parsing certain attributes in specific contexts where they would
337-
not normally be late parsed.
337+
not normally be late parsed. Currently this allows late parsing the
338+
`counted_by` attribute in C. See `Attribute Changes in Clang`_.
338339

339340
- ``-fseparate-named-sections`` uses separate unique sections for global
340341
symbols in named special sections (i.e. symbols annotated with
@@ -423,6 +424,24 @@ Attribute Changes in Clang
423424
- The ``clspv_libclc_builtin`` attribute has been added to allow clspv
424425
(`OpenCL-C to Vulkan SPIR-V compiler <https://github.com/google/clspv>`_) to identify functions coming from libclc
425426
(`OpenCL-C builtin library <https://libclc.llvm.org>`_).
427+
- The ``counted_by`` attribute is now allowed on pointers that are members of a
428+
struct in C.
429+
430+
- The ``counted_by`` attribute can now be late parsed in C when
431+
``-fexperimental-late-parse-attributes`` is passed but only when attribute is
432+
used in the declaration attribute position. This allows using the
433+
attribute on existing code where it previously impossible to do so without
434+
re-ordering struct field declarations would break ABI as shown below.
435+
436+
.. code-block:: c
437+
438+
struct BufferTy {
439+
/* Refering to `count` requires late parsing */
440+
char* buffer __counted_by(count);
441+
/* Swapping `buffer` and `count` to avoid late parsing would break ABI */
442+
size_t count;
443+
};
444+
426445
427446
Improvements to Clang's diagnostics
428447
-----------------------------------

clang/include/clang/AST/Type.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,6 +2515,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
25152515
bool isRecordType() const;
25162516
bool isClassType() const;
25172517
bool isStructureType() const;
2518+
bool isStructureTypeWithFlexibleArrayMember() const;
25182519
bool isObjCBoxableRecordType() const;
25192520
bool isInterfaceType() const;
25202521
bool isStructureOrClassType() const;

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2257,7 +2257,8 @@ def TypeNullUnspecified : TypeAttr {
22572257
def CountedBy : DeclOrTypeAttr {
22582258
let Spellings = [Clang<"counted_by">];
22592259
let Subjects = SubjectList<[Field], ErrorDiag>;
2260-
let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel">];
2260+
let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>];
2261+
let LateParsed = LateAttrParseExperimentalExt;
22612262
let ParseArgumentsAsUnevaluated = 1;
22622263
let Documentation = [CountedByDocs];
22632264
let LangOpts = [COnly];

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,10 @@ def FunctionMultiVersioning
14451445

14461446
def NoDeref : DiagGroup<"noderef">;
14471447

1448+
// -fbounds-safety and bounds annotation related warnings
1449+
def BoundsSafetyCountedByEltTyUnknownSize :
1450+
DiagGroup<"bounds-safety-counted-by-elt-type-unknown-size">;
1451+
14481452
// A group for cross translation unit static analysis related warnings.
14491453
def CrossTU : DiagGroup<"ctu">;
14501454

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6544,8 +6544,10 @@ def warn_superclass_variable_sized_type_not_at_end : Warning<
65446544

65456545
def err_flexible_array_count_not_in_same_struct : Error<
65466546
"'counted_by' field %0 isn't within the same struct as the flexible array">;
6547-
def err_counted_by_attr_not_on_flexible_array_member : Error<
6548-
"'counted_by' only applies to C99 flexible array members">;
6547+
def err_counted_by_attr_not_on_ptr_or_flexible_array_member : Error<
6548+
"'counted_by' only applies to pointers or C99 flexible array members">;
6549+
def err_counted_by_attr_on_array_not_flexible_array_member : Error<
6550+
"'counted_by' on arrays only applies to C99 flexible array members">;
65496551
def err_counted_by_attr_refer_to_itself : Error<
65506552
"'counted_by' cannot refer to the flexible array member %0">;
65516553
def err_counted_by_must_be_in_structure : Error<
@@ -6560,6 +6562,23 @@ def err_counted_by_attr_refer_to_union : Error<
65606562
"'counted_by' argument cannot refer to a union member">;
65616563
def note_flexible_array_counted_by_attr_field : Note<
65626564
"field %0 declared here">;
6565+
def err_counted_by_attr_pointee_unknown_size : Error<
6566+
"'counted_by' %select{cannot|should not}3 be applied to %select{"
6567+
"a pointer with pointee|" // pointer
6568+
"an array with element}0" // array
6569+
" of unknown size because %1 is %select{"
6570+
"an incomplete type|" // CountedByInvalidPointeeTypeKind::INCOMPLETE
6571+
"a sizeless type|" // CountedByInvalidPointeeTypeKind::SIZELESS
6572+
"a function type|" // CountedByInvalidPointeeTypeKind::FUNCTION
6573+
// CountedByInvalidPointeeTypeKind::FLEXIBLE_ARRAY_MEMBER
6574+
"a struct type with a flexible array member"
6575+
"%select{|. This will be an error in a future compiler version}3"
6576+
""
6577+
"}2">;
6578+
6579+
def warn_counted_by_attr_elt_type_unknown_size :
6580+
Warning<err_counted_by_attr_pointee_unknown_size.Summary>,
6581+
InGroup<BoundsSafetyCountedByEltTyUnknownSize>;
65636582

65646583
let CategoryName = "ARC Semantic Issue" in {
65656584

clang/include/clang/Parse/Parser.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1646,8 +1646,12 @@ class Parser : public CodeCompletionHandler {
16461646
void ParseLexedAttributes(ParsingClass &Class);
16471647
void ParseLexedAttributeList(LateParsedAttrList &LAs, Decl *D,
16481648
bool EnterScope, bool OnDefinition);
1649+
void ParseLexedCAttributeList(LateParsedAttrList &LA, bool EnterScope,
1650+
ParsedAttributes *OutAttrs = nullptr);
16491651
void ParseLexedAttribute(LateParsedAttribute &LA,
16501652
bool EnterScope, bool OnDefinition);
1653+
void ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
1654+
ParsedAttributes *OutAttrs = nullptr);
16511655
void ParseLexedMethodDeclarations(ParsingClass &Class);
16521656
void ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM);
16531657
void ParseLexedMethodDefs(ParsingClass &Class);
@@ -2534,7 +2538,8 @@ class Parser : public CodeCompletionHandler {
25342538

25352539
void ParseStructDeclaration(
25362540
ParsingDeclSpec &DS,
2537-
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback);
2541+
llvm::function_ref<Decl *(ParsingFieldDeclarator &)> FieldsCallback,
2542+
LateParsedAttrList *LateFieldAttrs = nullptr);
25382543

25392544
DeclGroupPtrTy ParseTopLevelStmtDecl();
25402545

@@ -3113,6 +3118,8 @@ class Parser : public CodeCompletionHandler {
31133118
SourceLocation ScopeLoc,
31143119
ParsedAttr::Form Form);
31153120

3121+
void DistributeCLateParsedAttrs(Decl *Dcl, LateParsedAttrList *LateAttrs);
3122+
31163123
void ParseBoundsAttribute(IdentifierInfo &AttrName,
31173124
SourceLocation AttrNameLoc, ParsedAttributes &Attrs,
31183125
IdentifierInfo *ScopeName, SourceLocation ScopeLoc,

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11403,7 +11403,8 @@ class Sema final : public SemaBase {
1140311403
QualType BuildMatrixType(QualType T, Expr *NumRows, Expr *NumColumns,
1140411404
SourceLocation AttrLoc);
1140511405

11406-
QualType BuildCountAttributedArrayType(QualType WrappedTy, Expr *CountExpr);
11406+
QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy,
11407+
Expr *CountExpr);
1140711408

1140811409
QualType BuildAddressSpaceAttr(QualType &T, LangAS ASIdx, Expr *AddrSpace,
1140911410
SourceLocation AttrLoc);

clang/lib/AST/Type.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,16 @@ bool Type::isStructureType() const {
632632
return false;
633633
}
634634

635+
bool Type::isStructureTypeWithFlexibleArrayMember() const {
636+
const auto *RT = getAs<RecordType>();
637+
if (!RT)
638+
return false;
639+
const auto *Decl = RT->getDecl();
640+
if (!Decl->isStruct())
641+
return false;
642+
return Decl->hasFlexibleArrayMember();
643+
}
644+
635645
bool Type::isObjCBoxableRecordType() const {
636646
if (const auto *RT = getAs<RecordType>())
637647
return RT->getDecl()->hasAttr<ObjCBoxableAttr>();

clang/lib/Parse/ParseDecl.cpp

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3312,6 +3312,19 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs,
33123312
}
33133313
}
33143314

3315+
void Parser::DistributeCLateParsedAttrs(Decl *Dcl,
3316+
LateParsedAttrList *LateAttrs) {
3317+
if (!LateAttrs)
3318+
return;
3319+
3320+
if (Dcl) {
3321+
for (auto *LateAttr : *LateAttrs) {
3322+
if (LateAttr->Decls.empty())
3323+
LateAttr->addDecl(Dcl);
3324+
}
3325+
}
3326+
}
3327+
33153328
/// Bounds attributes (e.g., counted_by):
33163329
/// AttrName '(' expression ')'
33173330
void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName,
@@ -4849,13 +4862,14 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS,
48494862
///
48504863
void Parser::ParseStructDeclaration(
48514864
ParsingDeclSpec &DS,
4852-
llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback) {
4865+
llvm::function_ref<Decl *(ParsingFieldDeclarator &)> FieldsCallback,
4866+
LateParsedAttrList *LateFieldAttrs) {
48534867

48544868
if (Tok.is(tok::kw___extension__)) {
48554869
// __extension__ silences extension warnings in the subexpression.
48564870
ExtensionRAIIObject O(Diags); // Use RAII to do this.
48574871
ConsumeToken();
4858-
return ParseStructDeclaration(DS, FieldsCallback);
4872+
return ParseStructDeclaration(DS, FieldsCallback, LateFieldAttrs);
48594873
}
48604874

48614875
// Parse leading attributes.
@@ -4920,10 +4934,12 @@ void Parser::ParseStructDeclaration(
49204934
}
49214935

49224936
// If attributes exist after the declarator, parse them.
4923-
MaybeParseGNUAttributes(DeclaratorInfo.D);
4937+
MaybeParseGNUAttributes(DeclaratorInfo.D, LateFieldAttrs);
49244938

49254939
// We're done with this declarator; invoke the callback.
4926-
FieldsCallback(DeclaratorInfo);
4940+
Decl *Field = FieldsCallback(DeclaratorInfo);
4941+
if (Field)
4942+
DistributeCLateParsedAttrs(Field, LateFieldAttrs);
49274943

49284944
// If we don't have a comma, it is either the end of the list (a ';')
49294945
// or an error, bail out.
@@ -4934,6 +4950,73 @@ void Parser::ParseStructDeclaration(
49344950
}
49354951
}
49364952

4953+
// TODO: All callers of this function should be moved to
4954+
// `Parser::ParseLexedAttributeList`.
4955+
void Parser::ParseLexedCAttributeList(LateParsedAttrList &LAs, bool EnterScope,
4956+
ParsedAttributes *OutAttrs) {
4957+
assert(LAs.parseSoon() &&
4958+
"Attribute list should be marked for immediate parsing.");
4959+
for (auto *LA : LAs) {
4960+
ParseLexedCAttribute(*LA, EnterScope, OutAttrs);
4961+
delete LA;
4962+
}
4963+
LAs.clear();
4964+
}
4965+
4966+
/// Finish parsing an attribute for which parsing was delayed.
4967+
/// This will be called at the end of parsing a class declaration
4968+
/// for each LateParsedAttribute. We consume the saved tokens and
4969+
/// create an attribute with the arguments filled in. We add this
4970+
/// to the Attribute list for the decl.
4971+
void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope,
4972+
ParsedAttributes *OutAttrs) {
4973+
// Create a fake EOF so that attribute parsing won't go off the end of the
4974+
// attribute.
4975+
Token AttrEnd;
4976+
AttrEnd.startToken();
4977+
AttrEnd.setKind(tok::eof);
4978+
AttrEnd.setLocation(Tok.getLocation());
4979+
AttrEnd.setEofData(LA.Toks.data());
4980+
LA.Toks.push_back(AttrEnd);
4981+
4982+
// Append the current token at the end of the new token stream so that it
4983+
// doesn't get lost.
4984+
LA.Toks.push_back(Tok);
4985+
PP.EnterTokenStream(LA.Toks, /*DisableMacroExpansion=*/true,
4986+
/*IsReinject=*/true);
4987+
// Drop the current token and bring the first cached one. It's the same token
4988+
// as when we entered this function.
4989+
ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true);
4990+
4991+
// TODO: Use `EnterScope`
4992+
(void)EnterScope;
4993+
4994+
ParsedAttributes Attrs(AttrFactory);
4995+
4996+
assert(LA.Decls.size() <= 1 &&
4997+
"late field attribute expects to have at most one declaration.");
4998+
4999+
// Dispatch based on the attribute and parse it
5000+
ParseGNUAttributeArgs(&LA.AttrName, LA.AttrNameLoc, Attrs, nullptr, nullptr,
5001+
SourceLocation(), ParsedAttr::Form::GNU(), nullptr);
5002+
5003+
for (auto *D : LA.Decls)
5004+
Actions.ActOnFinishDelayedAttribute(getCurScope(), D, Attrs);
5005+
5006+
// Due to a parsing error, we either went over the cached tokens or
5007+
// there are still cached tokens left, so we skip the leftover tokens.
5008+
while (Tok.isNot(tok::eof))
5009+
ConsumeAnyToken();
5010+
5011+
// Consume the fake EOF token if it's there
5012+
if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData())
5013+
ConsumeAnyToken();
5014+
5015+
if (OutAttrs) {
5016+
OutAttrs->takeAllFrom(Attrs);
5017+
}
5018+
}
5019+
49375020
/// ParseStructUnionBody
49385021
/// struct-contents:
49395022
/// struct-declaration-list
@@ -4957,6 +5040,11 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
49575040
ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope);
49585041
Actions.ActOnTagStartDefinition(getCurScope(), TagDecl);
49595042

5043+
// `LateAttrParseExperimentalExtOnly=true` requests that only attributes
5044+
// marked with `LateAttrParseExperimentalExt` are late parsed.
5045+
LateParsedAttrList LateFieldAttrs(/*PSoon=*/true,
5046+
/*LateAttrParseExperimentalExtOnly=*/true);
5047+
49605048
// While we still have something to read, read the declarations in the struct.
49615049
while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
49625050
Tok.isNot(tok::eof)) {
@@ -5007,18 +5095,19 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
50075095
}
50085096

50095097
if (!Tok.is(tok::at)) {
5010-
auto CFieldCallback = [&](ParsingFieldDeclarator &FD) {
5098+
auto CFieldCallback = [&](ParsingFieldDeclarator &FD) -> Decl * {
50115099
// Install the declarator into the current TagDecl.
50125100
Decl *Field =
50135101
Actions.ActOnField(getCurScope(), TagDecl,
50145102
FD.D.getDeclSpec().getSourceRange().getBegin(),
50155103
FD.D, FD.BitfieldSize);
50165104
FD.complete(Field);
5105+
return Field;
50175106
};
50185107

50195108
// Parse all the comma separated declarators.
50205109
ParsingDeclSpec DS(*this);
5021-
ParseStructDeclaration(DS, CFieldCallback);
5110+
ParseStructDeclaration(DS, CFieldCallback, &LateFieldAttrs);
50225111
} else { // Handle @defs
50235112
ConsumeToken();
50245113
if (!Tok.isObjCAtKeyword(tok::objc_defs)) {
@@ -5059,7 +5148,10 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc,
50595148

50605149
ParsedAttributes attrs(AttrFactory);
50615150
// If attributes exist after struct contents, parse them.
5062-
MaybeParseGNUAttributes(attrs);
5151+
MaybeParseGNUAttributes(attrs, &LateFieldAttrs);
5152+
5153+
// Late parse field attributes if necessary.
5154+
ParseLexedCAttributeList(LateFieldAttrs, /*EnterScope=*/false);
50635155

50645156
SmallVector<Decl *, 32> FieldDecls(TagDecl->fields());
50655157

clang/lib/Parse/ParseObjc.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -780,16 +780,16 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
780780
}
781781

782782
bool addedToDeclSpec = false;
783-
auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) {
783+
auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) -> Decl * {
784784
if (FD.D.getIdentifier() == nullptr) {
785785
Diag(AtLoc, diag::err_objc_property_requires_field_name)
786786
<< FD.D.getSourceRange();
787-
return;
787+
return nullptr;
788788
}
789789
if (FD.BitfieldSize) {
790790
Diag(AtLoc, diag::err_objc_property_bitfield)
791791
<< FD.D.getSourceRange();
792-
return;
792+
return nullptr;
793793
}
794794

795795
// Map a nullability property attribute to a context-sensitive keyword
@@ -818,6 +818,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey,
818818
MethodImplKind);
819819

820820
FD.complete(Property);
821+
return Property;
821822
};
822823

823824
// Parse all the comma separated declarators.
@@ -2013,7 +2014,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl,
20132014
continue;
20142015
}
20152016

2016-
auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) {
2017+
auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) -> Decl * {
20172018
assert(getObjCDeclContext() == interfaceDecl &&
20182019
"Ivar should have interfaceDecl as its decl context");
20192020
// Install the declarator into the interface decl.
@@ -2024,6 +2025,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl,
20242025
if (Field)
20252026
AllIvarDecls.push_back(Field);
20262027
FD.complete(Field);
2028+
return Field;
20272029
};
20282030

20292031
// Parse all the comma separated declarators.

0 commit comments

Comments
 (0)