Skip to content

Commit ec02422

Browse files
committed
[Attributes] Support Attributes being declared as supporting an experimental late parsing mode "extension"
This patch changes the `LateParsed` field of `Attr` in `Attr.td` to be an instantiation of the new `LateAttrParseKind` class. The instation can be one of the following: * `LateAttrParsingNever` - Corresponds with the false value of `LateParsed` prior to this patch (the default for an attribute). * `LateAttrParseStandard` - Corresponds with the true value of `LateParsed` prior to this patch. * `LateAttrParseExperimentalExt` - A new mode described below. `LateAttrParseExperimentalExt` is an experimental extension to `LateAttrParseStandard`. Essentially this allows `Parser::ParseGNUAttributes(...)` to distinguish between these cases: 1. Only `LateAttrParseExperimentalExt` attributes should be late parsed. 2. Both `LateAttrParseExperimentalExt` and `LateAttrParseStandard` attributes should be late parsed. Callers (and indirect callers) of `Parser::ParseGNUAttributes(...)` indicate the desired behavior by setting a flag in the `LateParsedAttrList` object that is passed to the function. In addition to the above, a new driver and frontend flag (`-fexperimental-late-parse-attributes`) with a corresponding LangOpt (`ExperimentalLateParseAttributes`) is added that changes how `LateAttrParseExperimentalExt` attributes are parsed. * When the flag is disabled (default), in cases where only `LateAttrParsingExperimentalOnly` late parsing is requested, the attribute will be parsed immediately (i.e. **NOT** late parsed). This allows the attribute to act just like a `LateAttrParseStandard` attribute when the flag is disabled. * When the flag is enabled, in cases where only `LateAttrParsingExperimentalOnly` late parsing is requested, the attribute will be late parsed. The motivation behind this change is to allow the new `counted_by` attribute (part of `-fbounds-safety`) to support late parsing but **only** when `-fexperimental-late-parse-attributes` is enabled. This attribute needs to support late parsing to allow it to refer to fields later in a struct definition (or function parameters declared later). However, there isn't a precedent for supporting late attribute parsing in C so this flag allows the new behavior to exist in Clang but not be on by default. This behavior was requested as part of the `-fbounds-safety` RFC process (https://discourse.llvm.org/t/rfc-enforcing-bounds-safety-in-c-fbounds-safety/70854/68). This patch doesn't introduce any uses of `LateAttrParseExperimentalExt`. This will be added for the `counted_by` attribute in a future patch (#87596). A consequence is the new behavior added in this patch is not yet testable. Hence, the lack of tests covering the new behavior. rdar://125400257
1 parent 9bd1085 commit ec02422

File tree

8 files changed

+252
-43
lines changed

8 files changed

+252
-43
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,48 @@ class AttrSubjectMatcherAggregateRule<AttrSubject subject> {
592592

593593
def SubjectMatcherForNamed : AttrSubjectMatcherAggregateRule<Named>;
594594

595+
// Late Attribute parsing mode enum
596+
class LateAttrParseKind <int val> {
597+
int Kind = val;
598+
}
599+
600+
// Never late parsed
601+
def LateAttrParseNever : LateAttrParseKind<0>;
602+
603+
// Standard late attribute parsing
604+
//
605+
// This is language dependent. For example:
606+
//
607+
// * For C++ enables late parsing of a declaration attributes
608+
// * For C does not enable late parsing of attributes
609+
//
610+
def LateAttrParseStandard: LateAttrParseKind<1>;
611+
612+
// Experimental extension to standard late attribute parsing
613+
//
614+
// This extension behaves like `LateAttrParseStandard` but allows
615+
// late parsing attributes in more contexts.
616+
//
617+
// In contexts where `LateAttrParseStandard` attributes are late
618+
// parsed, `LateAttrParseExperimentalExt` attributes will also
619+
// be late parsed.
620+
//
621+
// In contexts that only late parse `LateAttrParseExperimentalExt` attributes
622+
// (see `LateParsedAttrList::lateAttrParseExperimentalExtOnly()`)
623+
//
624+
// * If `-fexperimental-late-parse-attributes`
625+
// (`LangOpts.ExperimentalLateParseAttributes`) is enabled the attribute
626+
// will be late parsed.
627+
// * If `-fexperimental-late-parse-attributes`
628+
// (`LangOpts.ExperimentalLateParseAttributes`) is disabled the attribute
629+
// will **not** be late parsed (i.e parsed immediately).
630+
//
631+
// The following contexts are supported:
632+
//
633+
// * TODO: Add contexts here when they are implemented.
634+
//
635+
def LateAttrParseExperimentalExt : LateAttrParseKind<2>;
636+
595637
class Attr {
596638
// The various ways in which an attribute can be spelled in source
597639
list<Spelling> Spellings;
@@ -603,8 +645,8 @@ class Attr {
603645
list<Accessor> Accessors = [];
604646
// Specify targets for spellings.
605647
list<TargetSpecificSpelling> TargetSpecificSpellings = [];
606-
// Set to true for attributes with arguments which require delayed parsing.
607-
bit LateParsed = 0;
648+
// Specifies the late parsing kind.
649+
LateAttrParseKind LateParsed = LateAttrParseNever;
608650
// Set to false to prevent an attribute from being propagated from a template
609651
// to the instantiation.
610652
bit Clone = 1;
@@ -3173,7 +3215,7 @@ def DiagnoseIf : InheritableAttr {
31733215
BoolArgument<"ArgDependent", 0, /*fake*/ 1>,
31743216
DeclArgument<Named, "Parent", 0, /*fake*/ 1>];
31753217
let InheritEvenIfAlreadyPresent = 1;
3176-
let LateParsed = 1;
3218+
let LateParsed = LateAttrParseStandard;
31773219
let AdditionalMembers = [{
31783220
bool isError() const { return diagnosticType == DT_Error; }
31793221
bool isWarning() const { return diagnosticType == DT_Warning; }
@@ -3472,7 +3514,7 @@ def AssertCapability : InheritableAttr {
34723514
let Spellings = [Clang<"assert_capability", 0>,
34733515
Clang<"assert_shared_capability", 0>];
34743516
let Subjects = SubjectList<[Function]>;
3475-
let LateParsed = 1;
3517+
let LateParsed = LateAttrParseStandard;
34763518
let TemplateDependent = 1;
34773519
let ParseArgumentsAsUnevaluated = 1;
34783520
let InheritEvenIfAlreadyPresent = 1;
@@ -3488,7 +3530,7 @@ def AcquireCapability : InheritableAttr {
34883530
GNU<"exclusive_lock_function">,
34893531
GNU<"shared_lock_function">];
34903532
let Subjects = SubjectList<[Function]>;
3491-
let LateParsed = 1;
3533+
let LateParsed = LateAttrParseStandard;
34923534
let TemplateDependent = 1;
34933535
let ParseArgumentsAsUnevaluated = 1;
34943536
let InheritEvenIfAlreadyPresent = 1;
@@ -3504,7 +3546,7 @@ def TryAcquireCapability : InheritableAttr {
35043546
Clang<"try_acquire_shared_capability", 0>];
35053547
let Subjects = SubjectList<[Function],
35063548
ErrorDiag>;
3507-
let LateParsed = 1;
3549+
let LateParsed = LateAttrParseStandard;
35083550
let TemplateDependent = 1;
35093551
let ParseArgumentsAsUnevaluated = 1;
35103552
let InheritEvenIfAlreadyPresent = 1;
@@ -3520,7 +3562,7 @@ def ReleaseCapability : InheritableAttr {
35203562
Clang<"release_generic_capability", 0>,
35213563
Clang<"unlock_function", 0>];
35223564
let Subjects = SubjectList<[Function]>;
3523-
let LateParsed = 1;
3565+
let LateParsed = LateAttrParseStandard;
35243566
let TemplateDependent = 1;
35253567
let ParseArgumentsAsUnevaluated = 1;
35263568
let InheritEvenIfAlreadyPresent = 1;
@@ -3539,7 +3581,7 @@ def RequiresCapability : InheritableAttr {
35393581
Clang<"requires_shared_capability", 0>,
35403582
Clang<"shared_locks_required", 0>];
35413583
let Args = [VariadicExprArgument<"Args">];
3542-
let LateParsed = 1;
3584+
let LateParsed = LateAttrParseStandard;
35433585
let TemplateDependent = 1;
35443586
let ParseArgumentsAsUnevaluated = 1;
35453587
let InheritEvenIfAlreadyPresent = 1;
@@ -3559,7 +3601,7 @@ def NoThreadSafetyAnalysis : InheritableAttr {
35593601
def GuardedBy : InheritableAttr {
35603602
let Spellings = [GNU<"guarded_by">];
35613603
let Args = [ExprArgument<"Arg">];
3562-
let LateParsed = 1;
3604+
let LateParsed = LateAttrParseStandard;
35633605
let TemplateDependent = 1;
35643606
let ParseArgumentsAsUnevaluated = 1;
35653607
let InheritEvenIfAlreadyPresent = 1;
@@ -3570,7 +3612,7 @@ def GuardedBy : InheritableAttr {
35703612
def PtGuardedBy : InheritableAttr {
35713613
let Spellings = [GNU<"pt_guarded_by">];
35723614
let Args = [ExprArgument<"Arg">];
3573-
let LateParsed = 1;
3615+
let LateParsed = LateAttrParseStandard;
35743616
let TemplateDependent = 1;
35753617
let ParseArgumentsAsUnevaluated = 1;
35763618
let InheritEvenIfAlreadyPresent = 1;
@@ -3581,7 +3623,7 @@ def PtGuardedBy : InheritableAttr {
35813623
def AcquiredAfter : InheritableAttr {
35823624
let Spellings = [GNU<"acquired_after">];
35833625
let Args = [VariadicExprArgument<"Args">];
3584-
let LateParsed = 1;
3626+
let LateParsed = LateAttrParseStandard;
35853627
let TemplateDependent = 1;
35863628
let ParseArgumentsAsUnevaluated = 1;
35873629
let InheritEvenIfAlreadyPresent = 1;
@@ -3592,7 +3634,7 @@ def AcquiredAfter : InheritableAttr {
35923634
def AcquiredBefore : InheritableAttr {
35933635
let Spellings = [GNU<"acquired_before">];
35943636
let Args = [VariadicExprArgument<"Args">];
3595-
let LateParsed = 1;
3637+
let LateParsed = LateAttrParseStandard;
35963638
let TemplateDependent = 1;
35973639
let ParseArgumentsAsUnevaluated = 1;
35983640
let InheritEvenIfAlreadyPresent = 1;
@@ -3603,7 +3645,7 @@ def AcquiredBefore : InheritableAttr {
36033645
def AssertExclusiveLock : InheritableAttr {
36043646
let Spellings = [GNU<"assert_exclusive_lock">];
36053647
let Args = [VariadicExprArgument<"Args">];
3606-
let LateParsed = 1;
3648+
let LateParsed = LateAttrParseStandard;
36073649
let TemplateDependent = 1;
36083650
let ParseArgumentsAsUnevaluated = 1;
36093651
let InheritEvenIfAlreadyPresent = 1;
@@ -3614,7 +3656,7 @@ def AssertExclusiveLock : InheritableAttr {
36143656
def AssertSharedLock : InheritableAttr {
36153657
let Spellings = [GNU<"assert_shared_lock">];
36163658
let Args = [VariadicExprArgument<"Args">];
3617-
let LateParsed = 1;
3659+
let LateParsed = LateAttrParseStandard;
36183660
let TemplateDependent = 1;
36193661
let ParseArgumentsAsUnevaluated = 1;
36203662
let InheritEvenIfAlreadyPresent = 1;
@@ -3627,7 +3669,7 @@ def AssertSharedLock : InheritableAttr {
36273669
def ExclusiveTrylockFunction : InheritableAttr {
36283670
let Spellings = [GNU<"exclusive_trylock_function">];
36293671
let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">];
3630-
let LateParsed = 1;
3672+
let LateParsed = LateAttrParseStandard;
36313673
let TemplateDependent = 1;
36323674
let ParseArgumentsAsUnevaluated = 1;
36333675
let InheritEvenIfAlreadyPresent = 1;
@@ -3640,7 +3682,7 @@ def ExclusiveTrylockFunction : InheritableAttr {
36403682
def SharedTrylockFunction : InheritableAttr {
36413683
let Spellings = [GNU<"shared_trylock_function">];
36423684
let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">];
3643-
let LateParsed = 1;
3685+
let LateParsed = LateAttrParseStandard;
36443686
let TemplateDependent = 1;
36453687
let ParseArgumentsAsUnevaluated = 1;
36463688
let InheritEvenIfAlreadyPresent = 1;
@@ -3651,7 +3693,7 @@ def SharedTrylockFunction : InheritableAttr {
36513693
def LockReturned : InheritableAttr {
36523694
let Spellings = [GNU<"lock_returned">];
36533695
let Args = [ExprArgument<"Arg">];
3654-
let LateParsed = 1;
3696+
let LateParsed = LateAttrParseStandard;
36553697
let TemplateDependent = 1;
36563698
let ParseArgumentsAsUnevaluated = 1;
36573699
let Subjects = SubjectList<[Function]>;
@@ -3661,7 +3703,7 @@ def LockReturned : InheritableAttr {
36613703
def LocksExcluded : InheritableAttr {
36623704
let Spellings = [GNU<"locks_excluded">];
36633705
let Args = [VariadicExprArgument<"Args">];
3664-
let LateParsed = 1;
3706+
let LateParsed = LateAttrParseStandard;
36653707
let TemplateDependent = 1;
36663708
let ParseArgumentsAsUnevaluated = 1;
36673709
let InheritEvenIfAlreadyPresent = 1;

clang/include/clang/Basic/LangOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ LANGOPT(ExperimentalLibrary, 1, 0, "enable unstable and experimental library fea
164164
LANGOPT(PointerAuthIntrinsics, 1, 0, "pointer authentication intrinsics")
165165

166166
LANGOPT(DoubleSquareBracketAttributes, 1, 0, "'[[]]' attributes extension for all language standard modes")
167+
LANGOPT(ExperimentalLateParseAttributes, 1, 0, "experimental late parsing of attributes")
167168

168169
COMPATIBLE_LANGOPT(RecoveryAST, 1, 1, "Preserve expressions in AST when encountering errors")
169170
COMPATIBLE_LANGOPT(RecoveryASTType, 1, 1, "Preserve the type in recovery expressions")

clang/include/clang/Driver/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1603,6 +1603,13 @@ defm double_square_bracket_attributes : BoolFOption<"double-square-bracket-attri
16031603
LangOpts<"DoubleSquareBracketAttributes">, DefaultTrue, PosFlag<SetTrue>,
16041604
NegFlag<SetFalse>>;
16051605

1606+
defm experimental_late_parse_attributes : BoolFOption<"experimental-late-parse-attributes",
1607+
LangOpts<"ExperimentalLateParseAttributes">, DefaultFalse,
1608+
PosFlag<SetTrue, [], [ClangOption], "Enable">,
1609+
NegFlag<SetFalse, [], [ClangOption], "Disable">,
1610+
BothFlags<[], [ClangOption, CC1Option],
1611+
" experimental late parsing of attributes">>;
1612+
16061613
defm autolink : BoolFOption<"autolink",
16071614
CodeGenOpts<"Autolink">, DefaultTrue,
16081615
NegFlag<SetFalse, [], [ClangOption, CC1Option],

clang/include/clang/Parse/Parser.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,14 +1371,23 @@ class Parser : public CodeCompletionHandler {
13711371
};
13721372

13731373
// A list of late-parsed attributes. Used by ParseGNUAttributes.
1374-
class LateParsedAttrList: public SmallVector<LateParsedAttribute *, 2> {
1374+
class LateParsedAttrList : public SmallVector<LateParsedAttribute *, 2> {
13751375
public:
1376-
LateParsedAttrList(bool PSoon = false) : ParseSoon(PSoon) { }
1376+
LateParsedAttrList(bool PSoon = false,
1377+
bool LateAttrParseExperimentalExtOnly = false)
1378+
: ParseSoon(PSoon),
1379+
LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly) {}
13771380

13781381
bool parseSoon() { return ParseSoon; }
1382+
/// returns true iff the attribute to be parsed should only be late parsed
1383+
/// if it is annotated with `LateAttrParseExperimentalExt`
1384+
bool lateAttrParseExperimentalExtOnly() {
1385+
return LateAttrParseExperimentalExtOnly;
1386+
}
13791387

13801388
private:
1381-
bool ParseSoon; // Are we planning to parse these shortly after creation?
1389+
bool ParseSoon; // Are we planning to parse these shortly after creation?
1390+
bool LateAttrParseExperimentalExtOnly;
13821391
};
13831392

13841393
/// Contains the lexed tokens of a member function definition

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7480,6 +7480,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
74807480
Args.addOptInFlag(CmdArgs, options::OPT_fsafe_buffer_usage_suggestions,
74817481
options::OPT_fno_safe_buffer_usage_suggestions);
74827482

7483+
Args.addOptInFlag(CmdArgs, options::OPT_fexperimental_late_parse_attributes,
7484+
options::OPT_fno_experimental_late_parse_attributes);
7485+
74837486
// Setup statistics file output.
74847487
SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D);
74857488
if (!StatsFile.empty()) {

clang/lib/Parse/ParseDecl.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,24 @@ static StringRef normalizeAttrName(StringRef Name) {
8989
return Name;
9090
}
9191

92-
/// isAttributeLateParsed - Return true if the attribute has arguments that
93-
/// require late parsing.
94-
static bool isAttributeLateParsed(const IdentifierInfo &II) {
92+
/// returns true iff attribute is annotated with `LateAttrParseExperimentalExt`
93+
/// in `Attr.td`.
94+
static bool IsAttributeLateParsedExperimentalExt(const IdentifierInfo &II) {
95+
#define CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST
96+
return
97+
llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
98+
#include "clang/Parse/AttrParserStringSwitches.inc"
99+
.Default(false);
100+
#undef CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST
101+
}
102+
103+
/// returns true iff attribute is annotated with `LateAttrParseStandard` in
104+
/// `Attr.td`.
105+
static bool isAttributeLateParsedStandard(const IdentifierInfo &II) {
95106
#define CLANG_ATTR_LATE_PARSED_LIST
96-
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
107+
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
97108
#include "clang/Parse/AttrParserStringSwitches.inc"
98-
.Default(false);
109+
.Default(false);
99110
#undef CLANG_ATTR_LATE_PARSED_LIST
100111
}
101112

@@ -220,8 +231,28 @@ void Parser::ParseGNUAttributes(ParsedAttributes &Attrs,
220231
continue;
221232
}
222233

234+
bool LateParse = false;
235+
if (!LateAttrs)
236+
LateParse = false;
237+
else {
238+
if (LateAttrs->lateAttrParseExperimentalExtOnly()) {
239+
// The caller requested that this attribute **only** be late
240+
// parsed for `LateAttrParseExperimentalExt` attributes. This will
241+
// only be late parsed if the experimental language option is enabled.
242+
LateParse = IsAttributeLateParsedExperimentalExt(*AttrName) &&
243+
getLangOpts().ExperimentalLateParseAttributes;
244+
} else {
245+
// The caller did not restrict late parsing to only
246+
// `LateAttrParseExperimentalExt` attributes so late parse
247+
// both `LateAttrParseStandard` and `LateAttrParseExperimentalExt`
248+
// attributes.
249+
LateParse = IsAttributeLateParsedExperimentalExt(*AttrName) ||
250+
isAttributeLateParsedStandard(*AttrName);
251+
}
252+
}
253+
223254
// Handle "parameterized" attributes
224-
if (!LateAttrs || !isAttributeLateParsed(*AttrName)) {
255+
if (!LateParse) {
225256
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, &EndLoc, nullptr,
226257
SourceLocation(), ParsedAttr::Form::GNU(), D);
227258
continue;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// RUN: %clang %s -c -fexperimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-ON
2+
// RUN: %clang %s -c -fno-experimental-late-parse-attributes -fexperimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-ON
3+
4+
// CHECK-ON: -cc1
5+
// CHECK-ON: -fexperimental-late-parse-attributes
6+
7+
// RUN: %clang %s -c 2>&1 -### | FileCheck %s -check-prefix=CHECK-OFF
8+
// RUN: %clang %s -c -fno-experimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-OFF
9+
// RUN: %clang %s -c -fexperimental-late-parse-attributes -fno-experimental-late-parse-attributes 2>&1 -### | FileCheck %s -check-prefix=CHECK-OFF
10+
11+
// CHECK-OFF: -cc1
12+
// CHECK-OFF-NOT: -fexperimental-late-parse-attributes

0 commit comments

Comments
 (0)