https://github.com/delcypher updated https://github.com/llvm/llvm-project/pull/87596
>From af735f3216dd5db9dcaf164892f3f573731701ec Mon Sep 17 00:00:00 2001 From: Yeoul Na <yeoul...@apple.com> Date: Wed, 3 Apr 2024 20:58:46 -0700 Subject: [PATCH 1/2] [BoundsSafety] WIP: Make 'counted_by' work for pointer fields; late parsing for 'counted_by' on decl attr position --- clang/include/clang/Basic/Attr.td | 3 +- clang/include/clang/Parse/Parser.h | 11 +- clang/include/clang/Sema/Sema.h | 2 +- clang/lib/Parse/ParseDecl.cpp | 108 ++++++++++++++++-- clang/lib/Parse/ParseObjc.cpp | 6 +- clang/lib/Sema/SemaDeclAttr.cpp | 11 +- clang/lib/Sema/SemaType.cpp | 6 +- clang/lib/Sema/TreeTransform.h | 2 +- clang/test/AST/attr-counted-by-late-parsed.c | 29 +++++ clang/test/Sema/attr-counted-by-late-parsed.c | 54 +++++++++ clang/test/Sema/attr-counted-by.c | 2 +- 11 files changed, 212 insertions(+), 22 deletions(-) create mode 100644 clang/test/AST/attr-counted-by-late-parsed.c create mode 100644 clang/test/Sema/attr-counted-by-late-parsed.c diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 6584460cf5685e..d6caa28938edef 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2197,7 +2197,8 @@ def TypeNullUnspecified : TypeAttr { def CountedBy : DeclOrTypeAttr { let Spellings = [Clang<"counted_by">]; let Subjects = SubjectList<[Field], ErrorDiag>; - let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel">]; + let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; + let LateParsed = 1; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; let LangOpts = [COnly]; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 580bf2a5d79df5..41fc5d5dc98a6b 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -1606,6 +1606,10 @@ class Parser : public CodeCompletionHandler { bool EnterScope, bool OnDefinition); void ParseLexedAttribute(LateParsedAttribute &LA, bool EnterScope, bool OnDefinition); + void ParseLexedCAttributeList(LateParsedAttrList &LA, bool EnterScope, + ParsedAttributes *OutAttrs = nullptr); + void ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, + ParsedAttributes *OutAttrs = nullptr); void ParseLexedMethodDeclarations(ParsingClass &Class); void ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM); void ParseLexedMethodDefs(ParsingClass &Class); @@ -2497,7 +2501,9 @@ class Parser : public CodeCompletionHandler { void ParseStructDeclaration( ParsingDeclSpec &DS, - llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback); + llvm::function_ref<void(ParsingFieldDeclarator &, Decl *&)> + FieldsCallback, + LateParsedAttrList *LateFieldAttrs = nullptr); DeclGroupPtrTy ParseTopLevelStmtDecl(); @@ -3074,6 +3080,9 @@ class Parser : public CodeCompletionHandler { SourceLocation ScopeLoc, ParsedAttr::Form Form); + void DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl, + LateParsedAttrList *LateAttrs); + void ParseBoundsAttribute(IdentifierInfo &AttrName, SourceLocation AttrNameLoc, ParsedAttributes &Attrs, IdentifierInfo *ScopeName, SourceLocation ScopeLoc, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 8c98d8c7fef7a7..de49f2344f017f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -11761,7 +11761,7 @@ class Sema final { QualType BuildMatrixType(QualType T, Expr *NumRows, Expr *NumColumns, SourceLocation AttrLoc); - QualType BuildCountAttributedArrayType(QualType WrappedTy, Expr *CountExpr); + QualType BuildCountAttributedArrayOrPointerType(QualType WrappedTy, Expr *CountExpr); QualType BuildAddressSpaceAttr(QualType &T, LangAS ASIdx, Expr *AddrSpace, SourceLocation AttrLoc); diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 0aa14b0510746b..1c2e6d447cf5d5 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3254,6 +3254,17 @@ void Parser::ParseAlignmentSpecifier(ParsedAttributes &Attrs, } } +void Parser::DistributeCLateParsedAttrs(Declarator &D, Decl *Dcl, + LateParsedAttrList *LateAttrs) { + if (!LateAttrs) + return; + + for (auto *LateAttr : *LateAttrs) { + if (LateAttr->Decls.empty()) + LateAttr->addDecl(Dcl); + } +} + /// Bounds attributes (e.g., counted_by): /// AttrName '(' expression ')' void Parser::ParseBoundsAttribute(IdentifierInfo &AttrName, @@ -4787,13 +4798,14 @@ static void DiagnoseCountAttributedTypeInUnnamedAnon(ParsingDeclSpec &DS, /// void Parser::ParseStructDeclaration( ParsingDeclSpec &DS, - llvm::function_ref<void(ParsingFieldDeclarator &)> FieldsCallback) { + llvm::function_ref<void(ParsingFieldDeclarator &, Decl *&)> FieldsCallback, + LateParsedAttrList *LateFieldAttrs) { if (Tok.is(tok::kw___extension__)) { // __extension__ silences extension warnings in the subexpression. ExtensionRAIIObject O(Diags); // Use RAII to do this. ConsumeToken(); - return ParseStructDeclaration(DS, FieldsCallback); + return ParseStructDeclaration(DS, FieldsCallback, LateFieldAttrs); } // Parse leading attributes. @@ -4858,10 +4870,12 @@ void Parser::ParseStructDeclaration( } // If attributes exist after the declarator, parse them. - MaybeParseGNUAttributes(DeclaratorInfo.D); + MaybeParseGNUAttributes(DeclaratorInfo.D, LateFieldAttrs); // We're done with this declarator; invoke the callback. - FieldsCallback(DeclaratorInfo); + Decl *Field = nullptr; + FieldsCallback(DeclaratorInfo, Field); + DistributeCLateParsedAttrs(DeclaratorInfo.D, Field, LateFieldAttrs); // If we don't have a comma, it is either the end of the list (a ';') // or an error, bail out. @@ -4872,6 +4886,79 @@ void Parser::ParseStructDeclaration( } } +// Parse all attributes in LA, and attach them to Decl D. +void Parser::ParseLexedCAttributeList(LateParsedAttrList &LA, bool EnterScope, ParsedAttributes *OutAttrs) { + assert(LA.parseSoon() && + "Attribute list should be marked for immediate parsing."); + for (unsigned i = 0, ni = LA.size(); i < ni; ++i) { + ParseLexedCAttribute(*LA[i], EnterScope, OutAttrs); + delete LA[i]; + } + LA.clear(); +} + +/// Finish parsing an attribute for which parsing was delayed. +/// This will be called at the end of parsing a class declaration +/// for each LateParsedAttribute. We consume the saved tokens and +/// create an attribute with the arguments filled in. We add this +/// to the Attribute list for the decl. +void Parser::ParseLexedCAttribute(LateParsedAttribute &LA, bool EnterScope, ParsedAttributes *OutAttrs) { + // Create a fake EOF so that attribute parsing won't go off the end of the + // attribute. + Token AttrEnd; + AttrEnd.startToken(); + AttrEnd.setKind(tok::eof); + AttrEnd.setLocation(Tok.getLocation()); + AttrEnd.setEofData(LA.Toks.data()); + LA.Toks.push_back(AttrEnd); + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + LA.Toks.push_back(Tok); + PP.EnterTokenStream(LA.Toks, true, /*IsReinject=*/true); + // Consume the previously pushed token. + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + + ParsedAttributes Attrs(AttrFactory); + + assert( + LA.Decls.size() < 2 && + "late field attribute expects to have at most a single declaration."); + + Decl *D = LA.Decls.empty() ? nullptr : LA.Decls[0]; + + // If the Decl is on a function, add function parameters to the scope. + { + std::unique_ptr<ParseScope> Scope; + EnterScope &= D && D->isFunctionOrFunctionTemplate(); + if (EnterScope) { + Scope.reset(new ParseScope(this, Scope::FnScope | Scope::DeclScope)); + Actions.ActOnReenterFunctionContext(Actions.CurScope, D); + } + ParseBoundsAttribute(LA.AttrName, LA.AttrNameLoc, Attrs, + /*ScopeName*/nullptr, SourceLocation(), + ParsedAttr::Form::GNU()); + if (EnterScope) { + Actions.ActOnExitFunctionContext(); + } + } + + for (unsigned i = 0, ni = LA.Decls.size(); i < ni; ++i) + Actions.ActOnFinishDelayedAttribute(getCurScope(), LA.Decls[i], Attrs); + + // Due to a parsing error, we either went over the cached tokens or + // there are still cached tokens left, so we skip the leftover tokens. + while (Tok.isNot(tok::eof)) + ConsumeAnyToken(); + + if (Tok.is(tok::eof) && Tok.getEofData() == AttrEnd.getEofData()) + ConsumeAnyToken(); + + if (OutAttrs) { + OutAttrs->takeAllFrom(Attrs); + } +} + /// ParseStructUnionBody /// struct-contents: /// struct-declaration-list @@ -4895,6 +4982,8 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, ParseScope StructScope(this, Scope::ClassScope|Scope::DeclScope); Actions.ActOnTagStartDefinition(getCurScope(), TagDecl); + LateParsedAttrList LateFieldAttrs; + // While we still have something to read, read the declarations in the struct. while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) { @@ -4945,18 +5034,19 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, } if (!Tok.is(tok::at)) { - auto CFieldCallback = [&](ParsingFieldDeclarator &FD) { + auto CFieldCallback = [&](ParsingFieldDeclarator &FD, Decl *&Dcl) { // Install the declarator into the current TagDecl. Decl *Field = Actions.ActOnField(getCurScope(), TagDecl, FD.D.getDeclSpec().getSourceRange().getBegin(), FD.D, FD.BitfieldSize); FD.complete(Field); + Dcl = Field; }; // Parse all the comma separated declarators. ParsingDeclSpec DS(*this); - ParseStructDeclaration(DS, CFieldCallback); + ParseStructDeclaration(DS, CFieldCallback, &LateFieldAttrs); } else { // Handle @defs ConsumeToken(); if (!Tok.isObjCAtKeyword(tok::objc_defs)) { @@ -4997,7 +5087,11 @@ void Parser::ParseStructUnionBody(SourceLocation RecordLoc, ParsedAttributes attrs(AttrFactory); // If attributes exist after struct contents, parse them. - MaybeParseGNUAttributes(attrs); + MaybeParseGNUAttributes(attrs, &LateFieldAttrs); + + assert(!getLangOpts().CPlusPlus); + for (auto LateAttr : LateFieldAttrs) + ParseLexedCAttribute(*LateAttr, true); SmallVector<Decl *, 32> FieldDecls(TagDecl->fields()); diff --git a/clang/lib/Parse/ParseObjc.cpp b/clang/lib/Parse/ParseObjc.cpp index 88bab0eb27a3ed..08cdd45e633ea1 100644 --- a/clang/lib/Parse/ParseObjc.cpp +++ b/clang/lib/Parse/ParseObjc.cpp @@ -778,7 +778,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, } bool addedToDeclSpec = false; - auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD) { + auto ObjCPropertyCallback = [&](ParsingFieldDeclarator &FD, Decl *&Dcl) { if (FD.D.getIdentifier() == nullptr) { Diag(AtLoc, diag::err_objc_property_requires_field_name) << FD.D.getSourceRange(); @@ -816,6 +816,7 @@ void Parser::ParseObjCInterfaceDeclList(tok::ObjCKeywordKind contextKey, MethodImplKind); FD.complete(Property); + Dcl = Property; }; // Parse all the comma separated declarators. @@ -2024,7 +2025,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl, continue; } - auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD) { + auto ObjCIvarCallback = [&](ParsingFieldDeclarator &FD, Decl *&Dcl) { assert(getObjCDeclContext() == interfaceDecl && "Ivar should have interfaceDecl as its decl context"); // Install the declarator into the interface decl. @@ -2035,6 +2036,7 @@ void Parser::ParseObjCClassInstanceVariables(ObjCContainerDecl *interfaceDecl, if (Field) AllIvarDecls.push_back(Field); FD.complete(Field); + Dcl = Field; }; // Parse all the comma separated declarators. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 8bce04640e748e..42c144c3ce5573 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8593,8 +8593,8 @@ static const RecordDecl *GetEnclosingNamedOrTopAnonRecord(const FieldDecl *FD) { } static bool -CheckCountExpr(Sema &S, FieldDecl *FD, Expr *E, - llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) { +CheckCountExprOnField(Sema &S, FieldDecl *FD, Expr *E, + llvm::SmallVectorImpl<TypeCoupledDeclRefInfo> &Decls) { if (FD->getParent()->isUnion()) { S.Diag(FD->getBeginLoc(), diag::err_counted_by_attr_in_union) << FD->getSourceRange(); @@ -8610,7 +8610,8 @@ CheckCountExpr(Sema &S, FieldDecl *FD, Expr *E, LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel = LangOptions::StrictFlexArraysLevelKind::IncompleteOnly; - if (!Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FD->getType(), + if (FD->getType()->isArrayType() && + !Decl::isFlexibleArrayMemberLike(S.getASTContext(), FD, FD->getType(), StrictFlexArraysLevel, true)) { // The "counted_by" attribute must be on a flexible array member. SourceRange SR = FD->getLocation(); @@ -8679,10 +8680,10 @@ static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) { return; llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls; - if (CheckCountExpr(S, FD, CountExpr, Decls)) + if (CheckCountExprOnField(S, FD, CountExpr, Decls)) return; - QualType CAT = S.BuildCountAttributedArrayType(FD->getType(), CountExpr); + QualType CAT = S.BuildCountAttributedArrayOrPointerType(FD->getType(), CountExpr); FD->setType(CAT); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 8762744396f4dd..6cfac4f9ec6c3b 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -9786,9 +9786,9 @@ BuildTypeCoupledDecls(Expr *E, Decls.push_back(TypeCoupledDeclRefInfo(CountDecl, /*IsDref*/ false)); } -QualType Sema::BuildCountAttributedArrayType(QualType WrappedTy, - Expr *CountExpr) { - assert(WrappedTy->isIncompleteArrayType()); +QualType Sema::BuildCountAttributedArrayOrPointerType(QualType WrappedTy, + Expr *CountExpr) { + assert(WrappedTy->isIncompleteArrayType() || WrappedTy->isPointerType()); llvm::SmallVector<TypeCoupledDeclRefInfo, 1> Decls; BuildTypeCoupledDecls(CountExpr, Decls); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a2568ad0f82cc2..97bf2e50fdc6cf 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -7308,7 +7308,7 @@ QualType TreeTransform<Derived>::TransformCountAttributedType( if (getDerived().AlwaysRebuild() || InnerTy != OldTy->desugar() || OldCount != NewCount) { // Currently, CountAttributedType can only wrap incomplete array types. - Result = SemaRef.BuildCountAttributedArrayType(InnerTy, NewCount); + Result = SemaRef.BuildCountAttributedArrayOrPointerType(InnerTy, NewCount); } TLB.push<CountAttributedTypeLoc>(Result); diff --git a/clang/test/AST/attr-counted-by-late-parsed.c b/clang/test/AST/attr-counted-by-late-parsed.c new file mode 100644 index 00000000000000..ada479189bc5b2 --- /dev/null +++ b/clang/test/AST/attr-counted-by-late-parsed.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_known; + +struct at_decl { + struct size_known *buf __counted_by(count); + int count; +}; +// CHECK-LABEL: struct at_decl definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: `-FieldDecl {{.*}} referenced count 'int' + +struct at_decl_anon_count { + struct size_known *buf __counted_by(count); + struct { + int count; + }; +}; + +// CHECK-LABEL: struct at_decl_anon_count definition +// CHECK-NEXT: |-FieldDecl {{.*}} buf 'struct size_known * __counted_by(count)':'struct size_known *' +// CHECK-NEXT: |-RecordDecl {{.*}} struct definition +// CHECK-NEXT: | `-FieldDecl {{.*}} count 'int' +// CHECK-NEXT: |-FieldDecl {{.*}} implicit 'struct at_decl_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-IndirectFieldDecl {{.*}} implicit referenced count 'int' +// CHECK-NEXT: |-Field {{.*}} '' 'struct at_decl_anon_count::(anonymous at {{.*}})' +// CHECK-NEXT: `-Field {{.*}} 'count' 'int' diff --git a/clang/test/Sema/attr-counted-by-late-parsed.c b/clang/test/Sema/attr-counted-by-late-parsed.c new file mode 100644 index 00000000000000..0c471e3f1520a8 --- /dev/null +++ b/clang/test/Sema/attr-counted-by-late-parsed.c @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#define __counted_by(f) __attribute__((counted_by(f))) + +struct size_unknown; + +struct at_pointer { + int count; + struct size_unknown *__counted_by(count) buf; // expected-error{{'counted_by' cannot be applied to an sized type}} +}; + +struct size_known { int dummy; }; + +struct at_nested_pointer { + // FIXME + struct size_known *__counted_by(count) *buf; // expected-error{{use of undeclared identifier 'count'}} + int count; +}; + +struct at_decl { + struct size_known *buf __counted_by(count); + int count; +}; + +struct at_pointer_anon_buf { + struct { + // FIXME + struct size_known *__counted_by(count) buf; // expected-error{{use of undeclared identifier 'count'}} + }; + int count; +}; + +struct at_decl_anon_buf { + struct { + // FIXME + struct size_known *buf __counted_by(count); // expected-error{{use of undeclared identifier 'count'}} + }; + int count; +}; + +struct at_pointer_anon_count { + // FIXME + struct size_known *__counted_by(count) buf; // expected-error{{use of undeclared identifier 'count'}} + struct { + int count; + }; +}; + +struct at_decl_anon_count { + struct size_known *buf __counted_by(count); + struct { + int count; + }; +}; \ No newline at end of file diff --git a/clang/test/Sema/attr-counted-by.c b/clang/test/Sema/attr-counted-by.c index d5d4ebf5573922..b0496af2fb4d8c 100644 --- a/clang/test/Sema/attr-counted-by.c +++ b/clang/test/Sema/attr-counted-by.c @@ -95,7 +95,7 @@ struct array_of_ints_count { struct not_a_fam { int count; - struct bar *non_fam __counted_by(count); // expected-error {{'counted_by' only applies to C99 flexible array members}} + struct bar *non_fam __counted_by(count); }; struct not_a_c99_fam { >From ef0838db5d24de4c2c1774a91d50014642dc5b46 Mon Sep 17 00:00:00 2001 From: Dan Liew <d...@su-root.co.uk> Date: Wed, 10 Apr 2024 17:18:03 -0700 Subject: [PATCH 2/2] [WIP] Add experimental late parsing mode --- clang/include/clang/Basic/Attr.td | 44 +++++++++------- clang/include/clang/Basic/LangOptions.def | 1 + clang/include/clang/Driver/Options.td | 7 +++ clang/include/clang/Parse/Parser.h | 4 ++ clang/lib/Parse/ParseDecl.cpp | 19 +++++-- clang/utils/TableGen/ClangAttrEmitter.cpp | 64 +++++++++++++++++------ 6 files changed, 103 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index d6caa28938edef..e31e386d68301a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -595,6 +595,14 @@ class AttrSubjectMatcherAggregateRule<AttrSubject subject> { def SubjectMatcherForNamed : AttrSubjectMatcherAggregateRule<Named>; +// Late Attribute parsing mode enum +class LateAttrParseMode <int val> { + int Mode = val; +} +def LateAttrParsingNever : LateAttrParseMode<0>; +def LateAttrParsingAlways: LateAttrParseMode<1>; +def LateAttrParsingExperimentalOnly : LateAttrParseMode<2>; + class Attr { // Specifies that when printed, this attribute is meaningful on the // 'left side' of the declaration. @@ -613,7 +621,7 @@ class Attr { // Specify targets for spellings. list<TargetSpecificSpelling> TargetSpecificSpellings = []; // Set to true for attributes with arguments which require delayed parsing. - bit LateParsed = 0; + LateAttrParseMode LateParsed = LateAttrParsingNever; // Set to false to prevent an attribute from being propagated from a template // to the instantiation. bit Clone = 1; @@ -2198,7 +2206,7 @@ def CountedBy : DeclOrTypeAttr { let Spellings = [Clang<"counted_by">]; let Subjects = SubjectList<[Field], ErrorDiag>; let Args = [ExprArgument<"Count">, IntArgument<"NestedLevel", 1>]; - let LateParsed = 1; + let LateParsed = LateAttrParsingExperimentalOnly; let ParseArgumentsAsUnevaluated = 1; let Documentation = [CountedByDocs]; let LangOpts = [COnly]; @@ -3186,7 +3194,7 @@ def DiagnoseIf : InheritableAttr { BoolArgument<"ArgDependent", 0, /*fake*/ 1>, DeclArgument<Named, "Parent", 0, /*fake*/ 1>]; let InheritEvenIfAlreadyPresent = 1; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let AdditionalMembers = [{ bool isError() const { return diagnosticType == DT_Error; } bool isWarning() const { return diagnosticType == DT_Warning; } @@ -3485,7 +3493,7 @@ def AssertCapability : InheritableAttr { let Spellings = [Clang<"assert_capability", 0>, Clang<"assert_shared_capability", 0>]; let Subjects = SubjectList<[Function]>; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3501,7 +3509,7 @@ def AcquireCapability : InheritableAttr { GNU<"exclusive_lock_function">, GNU<"shared_lock_function">]; let Subjects = SubjectList<[Function]>; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3517,7 +3525,7 @@ def TryAcquireCapability : InheritableAttr { Clang<"try_acquire_shared_capability", 0>]; let Subjects = SubjectList<[Function], ErrorDiag>; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3533,7 +3541,7 @@ def ReleaseCapability : InheritableAttr { Clang<"release_generic_capability", 0>, Clang<"unlock_function", 0>]; let Subjects = SubjectList<[Function]>; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3552,7 +3560,7 @@ def RequiresCapability : InheritableAttr { Clang<"requires_shared_capability", 0>, Clang<"shared_locks_required", 0>]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3572,7 +3580,7 @@ def NoThreadSafetyAnalysis : InheritableAttr { def GuardedBy : InheritableAttr { let Spellings = [GNU<"guarded_by">]; let Args = [ExprArgument<"Arg">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3583,7 +3591,7 @@ def GuardedBy : InheritableAttr { def PtGuardedBy : InheritableAttr { let Spellings = [GNU<"pt_guarded_by">]; let Args = [ExprArgument<"Arg">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3594,7 +3602,7 @@ def PtGuardedBy : InheritableAttr { def AcquiredAfter : InheritableAttr { let Spellings = [GNU<"acquired_after">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3605,7 +3613,7 @@ def AcquiredAfter : InheritableAttr { def AcquiredBefore : InheritableAttr { let Spellings = [GNU<"acquired_before">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3616,7 +3624,7 @@ def AcquiredBefore : InheritableAttr { def AssertExclusiveLock : InheritableAttr { let Spellings = [GNU<"assert_exclusive_lock">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3627,7 +3635,7 @@ def AssertExclusiveLock : InheritableAttr { def AssertSharedLock : InheritableAttr { let Spellings = [GNU<"assert_shared_lock">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3640,7 +3648,7 @@ def AssertSharedLock : InheritableAttr { def ExclusiveTrylockFunction : InheritableAttr { let Spellings = [GNU<"exclusive_trylock_function">]; let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3653,7 +3661,7 @@ def ExclusiveTrylockFunction : InheritableAttr { def SharedTrylockFunction : InheritableAttr { let Spellings = [GNU<"shared_trylock_function">]; let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; @@ -3664,7 +3672,7 @@ def SharedTrylockFunction : InheritableAttr { def LockReturned : InheritableAttr { let Spellings = [GNU<"lock_returned">]; let Args = [ExprArgument<"Arg">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let Subjects = SubjectList<[Function]>; @@ -3674,7 +3682,7 @@ def LockReturned : InheritableAttr { def LocksExcluded : InheritableAttr { let Spellings = [GNU<"locks_excluded">]; let Args = [VariadicExprArgument<"Args">]; - let LateParsed = 1; + let LateParsed = LateAttrParsingAlways; let TemplateDependent = 1; let ParseArgumentsAsUnevaluated = 1; let InheritEvenIfAlreadyPresent = 1; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 8ef6700ecdc78e..ed483fe8df131a 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -164,6 +164,7 @@ LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library fea LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics") LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes") +LANGOPT(ExperimentalLateParseAttributes, 1, 0, "enable experimental late parsing of attributes") COMPATIBLE_LANGOPT(RecoveryAST, 1, 1, "Preserve expressions in AST when encountering errors") COMPATIBLE_LANGOPT(RecoveryASTType, 1, 1, "Preserve the type in recovery expressions") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index c3e90a70925b78..249b13d7d8703d 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -1599,6 +1599,13 @@ defm double_square_bracket_attributes : BoolFOption<"double-square-bracket-attri LangOpts<"DoubleSquareBracketAttributes">, DefaultTrue, PosFlag<SetTrue>, NegFlag<SetFalse>>; +defm experimental_late_parse_attributes : BoolFOption<"experimental-late-parse-attributes", + LangOpts<"ExperimentalLateParseAttributes">, DefaultFalse, + PosFlag<SetTrue, [], [ClangOption], "Enable ">, + NegFlag<SetFalse, [], [ClangOption], "Disable ">, + BothFlags<[], [ClangOption, CC1Option], + " experimental late parsing of attributes">>; + defm autolink : BoolFOption<"autolink", CodeGenOpts<"Autolink">, DefaultTrue, NegFlag<SetFalse, [], [ClangOption, CC1Option], diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 41fc5d5dc98a6b..d88b8fbe2e526d 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2968,6 +2968,10 @@ class Parser : public CodeCompletionHandler { SourceLocation AttrNameLoc, SourceLocation *EndLoc); + /// isAttributeLateParsed - Return true if the attribute has arguments that + /// require late parsing. + bool isAttributeLateParsed(const IdentifierInfo &II); + IdentifierInfo *TryParseCXX11AttributeIdentifier( SourceLocation &Loc, Sema::AttributeCompletion Completion = Sema::AttributeCompletion::None, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 1c2e6d447cf5d5..d61276be4627a4 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -91,11 +91,24 @@ static StringRef normalizeAttrName(StringRef Name) { /// isAttributeLateParsed - Return true if the attribute has arguments that /// require late parsing. -static bool isAttributeLateParsed(const IdentifierInfo &II) { +bool Parser::isAttributeLateParsed(const IdentifierInfo &II) { + // Some attributes might only be late parsed when in the experimental + // language mode. + if (getLangOpts().ExperimentalLateParseAttributes) { +#define CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_LIST + bool IsExperimentalLateParseAttr = + llvm::StringSwitch<bool>(normalizeAttrName(II.getName())) +#include "clang/Parse/AttrParserStringSwitches.inc" + .Default(false); +#undef CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_LIST + if (IsExperimentalLateParseAttr) + return true; + } + #define CLANG_ATTR_LATE_PARSED_LIST - return llvm::StringSwitch<bool>(normalizeAttrName(II.getName())) + return llvm::StringSwitch<bool>(normalizeAttrName(II.getName())) #include "clang/Parse/AttrParserStringSwitches.inc" - .Default(false); + .Default(false); #undef CLANG_ATTR_LATE_PARSED_LIST } diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index eb5c34d15693d7..738b8beb2fd9ff 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -1823,27 +1823,43 @@ void WriteSemanticSpellingSwitch(const std::string &VarName, } // Emits the LateParsed property for attributes. -static void emitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) { - OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n"; - std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr"); +static void emitClangAttrLateParsedListImpl(RecordKeeper &Records, + raw_ostream &OS, + unsigned LateParseMode) { + std::vector<Record *> Attrs = Records.getAllDerivedDefinitions("Attr"); for (const auto *Attr : Attrs) { - bool LateParsed = Attr->getValueAsBit("LateParsed"); + unsigned LateParsed = + Attr->getValueAsDef("LateParsed")->getValueAsInt("Mode"); - if (LateParsed) { - std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr); + if (LateParsed != LateParseMode) + continue; - // FIXME: Handle non-GNU attributes - for (const auto &I : Spellings) { - if (I.variety() != "GNU") - continue; - OS << ".Case(\"" << I.name() << "\", " << LateParsed << ")\n"; - } + std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(*Attr); + + // FIXME: Handle non-GNU attributes + for (const auto &I : Spellings) { + if (I.variety() != "GNU") + continue; + OS << ".Case(\"" << I.name() << "\", 1)\n"; } } +} + +static void emitClangAttrLateParsedList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_LATE_PARSED_LIST)\n"; + emitClangAttrLateParsedListImpl(Records, OS, 1); OS << "#endif // CLANG_ATTR_LATE_PARSED_LIST\n\n"; } +static void emitClangAttrLateParsedExperimentalList(RecordKeeper &Records, + raw_ostream &OS) { + OS << "#if defined(CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_LIST)\n"; + emitClangAttrLateParsedListImpl(Records, OS, 2); + OS << "#endif // CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_LIST\n\n"; +} + static bool hasGNUorCXX11Spelling(const Record &Attribute) { std::vector<FlattenedSpelling> Spellings = GetFlattenedSpellings(Attribute); for (const auto &I : Spellings) { @@ -2102,7 +2118,7 @@ bool PragmaClangAttributeSupport::isAttributedSupported( // Opt-out rules: // An attribute requires delayed parsing (LateParsed is on) - if (Attribute.getValueAsBit("LateParsed")) + if (Attribute.getValueAsDef("LateParsed")->getValueAsInt("Mode")) // FIXME return false; // An attribute has no GNU/CXX11 spelling if (!hasGNUorCXX11Spelling(Attribute)) @@ -2885,8 +2901,25 @@ static void emitAttributes(RecordKeeper &Records, raw_ostream &OS, return; } OS << "\n : " << SuperName << "(Ctx, CommonInfo, "; - OS << "attr::" << R.getName() << ", " - << (R.getValueAsBit("LateParsed") ? "true" : "false"); + OS << "attr::" << R.getName() << ", "; + + // Handle different late parsing modes. + unsigned LateParseMode = + R.getValueAsDef("LateParsed")->getValueAsInt("Mode"); + switch (LateParseMode) { + case 0: // LateAttrParsingNever + case 1: // LateAttrParsingAlways + OS << LateParseMode; + break; + case 2: // LateAttrParsingExperimentalOnly + // Emit code that determines if Late Parsing is enabled based on + // the LangOpts. + OS << "Ctx.getLangOpts().ExperimentalLateParseAttributes"; + break; + default: + llvm_unreachable("Unhandled LateParseMode"); + } + if (Inheritable) { OS << ", " << (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true" @@ -4874,6 +4907,7 @@ void EmitClangAttrParserStringSwitches(RecordKeeper &Records, raw_ostream &OS) { emitClangAttrAcceptsExprPack(Records, OS); emitClangAttrTypeArgList(Records, OS); emitClangAttrLateParsedList(Records, OS); + emitClangAttrLateParsedExperimentalList(Records, OS); } void EmitClangAttrSubjectMatchRulesParserStringSwitches(RecordKeeper &Records, _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits