Skip to content

Commit a6d390c

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 a6d390c

File tree

9 files changed

+254
-42
lines changed

9 files changed

+254
-42
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ New Compiler Flags
214214
This diagnostic can be disabled to make ``-Wmissing-field-initializers`` behave
215215
like it did before Clang 18.x. Fixes (`#56628 <https://github.com/llvm/llvm-project/issues/68933>`_)
216216

217+
- ``-fexperimental-late-parse-attributes`` enables an experimental feature to
218+
allow late parsing certain attributes in specific contexts where they would
219+
not normally be late parsed.
220+
217221
Deprecated Compiler Flags
218222
-------------------------
219223

clang/include/clang/Basic/Attr.td

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

593593
def SubjectMatcherForNamed : AttrSubjectMatcherAggregateRule<Named>;
594594

595+
// Enumeration specifying what kind of behavior should be used for late
596+
// parsing of attributes.
597+
class LateAttrParseKind <int val> {
598+
int Kind = val;
599+
}
600+
601+
// Never late parsed
602+
def LateAttrParseNever : LateAttrParseKind<0>;
603+
604+
// Standard late attribute parsing
605+
//
606+
// This is language dependent. For example:
607+
//
608+
// * For C++ enables late parsing of a declaration attributes
609+
// * For C does not enable late parsing of attributes
610+
//
611+
def LateAttrParseStandard : LateAttrParseKind<1>;
612+
613+
// Experimental extension to standard late attribute parsing
614+
//
615+
// This extension behaves like `LateAttrParseStandard` but allows
616+
// late parsing attributes in more contexts.
617+
//
618+
// In contexts where `LateAttrParseStandard` attributes are late
619+
// parsed, `LateAttrParseExperimentalExt` attributes will also
620+
// be late parsed.
621+
//
622+
// In contexts that only late parse `LateAttrParseExperimentalExt` attributes
623+
// (see `LateParsedAttrList::lateAttrParseExperimentalExtOnly()`)
624+
//
625+
// * If `-fexperimental-late-parse-attributes`
626+
// (`LangOpts.ExperimentalLateParseAttributes`) is enabled the attribute
627+
// will be late parsed.
628+
// * If `-fexperimental-late-parse-attributes`
629+
// (`LangOpts.ExperimentalLateParseAttributes`) is disabled the attribute
630+
// will **not** be late parsed (i.e parsed immediately).
631+
//
632+
// The following contexts are supported:
633+
//
634+
// * TODO: Add contexts here when they are implemented.
635+
//
636+
def LateAttrParseExperimentalExt : LateAttrParseKind<2>;
637+
595638
class Attr {
596639
// The various ways in which an attribute can be spelled in source
597640
list<Spelling> Spellings;
@@ -603,8 +646,8 @@ class Attr {
603646
list<Accessor> Accessors = [];
604647
// Specify targets for spellings.
605648
list<TargetSpecificSpelling> TargetSpecificSpellings = [];
606-
// Set to true for attributes with arguments which require delayed parsing.
607-
bit LateParsed = 0;
649+
// Specifies the late parsing kind.
650+
LateAttrParseKind LateParsed = LateAttrParseNever;
608651
// Set to false to prevent an attribute from being propagated from a template
609652
// to the instantiation.
610653
bit Clone = 1;
@@ -3173,7 +3216,7 @@ def DiagnoseIf : InheritableAttr {
31733216
BoolArgument<"ArgDependent", 0, /*fake*/ 1>,
31743217
DeclArgument<Named, "Parent", 0, /*fake*/ 1>];
31753218
let InheritEvenIfAlreadyPresent = 1;
3176-
let LateParsed = 1;
3219+
let LateParsed = LateAttrParseStandard;
31773220
let AdditionalMembers = [{
31783221
bool isError() const { return diagnosticType == DT_Error; }
31793222
bool isWarning() const { return diagnosticType == DT_Warning; }
@@ -3472,7 +3515,7 @@ def AssertCapability : InheritableAttr {
34723515
let Spellings = [Clang<"assert_capability", 0>,
34733516
Clang<"assert_shared_capability", 0>];
34743517
let Subjects = SubjectList<[Function]>;
3475-
let LateParsed = 1;
3518+
let LateParsed = LateAttrParseStandard;
34763519
let TemplateDependent = 1;
34773520
let ParseArgumentsAsUnevaluated = 1;
34783521
let InheritEvenIfAlreadyPresent = 1;
@@ -3488,7 +3531,7 @@ def AcquireCapability : InheritableAttr {
34883531
GNU<"exclusive_lock_function">,
34893532
GNU<"shared_lock_function">];
34903533
let Subjects = SubjectList<[Function]>;
3491-
let LateParsed = 1;
3534+
let LateParsed = LateAttrParseStandard;
34923535
let TemplateDependent = 1;
34933536
let ParseArgumentsAsUnevaluated = 1;
34943537
let InheritEvenIfAlreadyPresent = 1;
@@ -3504,7 +3547,7 @@ def TryAcquireCapability : InheritableAttr {
35043547
Clang<"try_acquire_shared_capability", 0>];
35053548
let Subjects = SubjectList<[Function],
35063549
ErrorDiag>;
3507-
let LateParsed = 1;
3550+
let LateParsed = LateAttrParseStandard;
35083551
let TemplateDependent = 1;
35093552
let ParseArgumentsAsUnevaluated = 1;
35103553
let InheritEvenIfAlreadyPresent = 1;
@@ -3520,7 +3563,7 @@ def ReleaseCapability : InheritableAttr {
35203563
Clang<"release_generic_capability", 0>,
35213564
Clang<"unlock_function", 0>];
35223565
let Subjects = SubjectList<[Function]>;
3523-
let LateParsed = 1;
3566+
let LateParsed = LateAttrParseStandard;
35243567
let TemplateDependent = 1;
35253568
let ParseArgumentsAsUnevaluated = 1;
35263569
let InheritEvenIfAlreadyPresent = 1;
@@ -3539,7 +3582,7 @@ def RequiresCapability : InheritableAttr {
35393582
Clang<"requires_shared_capability", 0>,
35403583
Clang<"shared_locks_required", 0>];
35413584
let Args = [VariadicExprArgument<"Args">];
3542-
let LateParsed = 1;
3585+
let LateParsed = LateAttrParseStandard;
35433586
let TemplateDependent = 1;
35443587
let ParseArgumentsAsUnevaluated = 1;
35453588
let InheritEvenIfAlreadyPresent = 1;
@@ -3559,7 +3602,7 @@ def NoThreadSafetyAnalysis : InheritableAttr {
35593602
def GuardedBy : InheritableAttr {
35603603
let Spellings = [GNU<"guarded_by">];
35613604
let Args = [ExprArgument<"Arg">];
3562-
let LateParsed = 1;
3605+
let LateParsed = LateAttrParseStandard;
35633606
let TemplateDependent = 1;
35643607
let ParseArgumentsAsUnevaluated = 1;
35653608
let InheritEvenIfAlreadyPresent = 1;
@@ -3570,7 +3613,7 @@ def GuardedBy : InheritableAttr {
35703613
def PtGuardedBy : InheritableAttr {
35713614
let Spellings = [GNU<"pt_guarded_by">];
35723615
let Args = [ExprArgument<"Arg">];
3573-
let LateParsed = 1;
3616+
let LateParsed = LateAttrParseStandard;
35743617
let TemplateDependent = 1;
35753618
let ParseArgumentsAsUnevaluated = 1;
35763619
let InheritEvenIfAlreadyPresent = 1;
@@ -3581,7 +3624,7 @@ def PtGuardedBy : InheritableAttr {
35813624
def AcquiredAfter : InheritableAttr {
35823625
let Spellings = [GNU<"acquired_after">];
35833626
let Args = [VariadicExprArgument<"Args">];
3584-
let LateParsed = 1;
3627+
let LateParsed = LateAttrParseStandard;
35853628
let TemplateDependent = 1;
35863629
let ParseArgumentsAsUnevaluated = 1;
35873630
let InheritEvenIfAlreadyPresent = 1;
@@ -3592,7 +3635,7 @@ def AcquiredAfter : InheritableAttr {
35923635
def AcquiredBefore : InheritableAttr {
35933636
let Spellings = [GNU<"acquired_before">];
35943637
let Args = [VariadicExprArgument<"Args">];
3595-
let LateParsed = 1;
3638+
let LateParsed = LateAttrParseStandard;
35963639
let TemplateDependent = 1;
35973640
let ParseArgumentsAsUnevaluated = 1;
35983641
let InheritEvenIfAlreadyPresent = 1;
@@ -3603,7 +3646,7 @@ def AcquiredBefore : InheritableAttr {
36033646
def AssertExclusiveLock : InheritableAttr {
36043647
let Spellings = [GNU<"assert_exclusive_lock">];
36053648
let Args = [VariadicExprArgument<"Args">];
3606-
let LateParsed = 1;
3649+
let LateParsed = LateAttrParseStandard;
36073650
let TemplateDependent = 1;
36083651
let ParseArgumentsAsUnevaluated = 1;
36093652
let InheritEvenIfAlreadyPresent = 1;
@@ -3614,7 +3657,7 @@ def AssertExclusiveLock : InheritableAttr {
36143657
def AssertSharedLock : InheritableAttr {
36153658
let Spellings = [GNU<"assert_shared_lock">];
36163659
let Args = [VariadicExprArgument<"Args">];
3617-
let LateParsed = 1;
3660+
let LateParsed = LateAttrParseStandard;
36183661
let TemplateDependent = 1;
36193662
let ParseArgumentsAsUnevaluated = 1;
36203663
let InheritEvenIfAlreadyPresent = 1;
@@ -3627,7 +3670,7 @@ def AssertSharedLock : InheritableAttr {
36273670
def ExclusiveTrylockFunction : InheritableAttr {
36283671
let Spellings = [GNU<"exclusive_trylock_function">];
36293672
let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">];
3630-
let LateParsed = 1;
3673+
let LateParsed = LateAttrParseStandard;
36313674
let TemplateDependent = 1;
36323675
let ParseArgumentsAsUnevaluated = 1;
36333676
let InheritEvenIfAlreadyPresent = 1;
@@ -3640,7 +3683,7 @@ def ExclusiveTrylockFunction : InheritableAttr {
36403683
def SharedTrylockFunction : InheritableAttr {
36413684
let Spellings = [GNU<"shared_trylock_function">];
36423685
let Args = [ExprArgument<"SuccessValue">, VariadicExprArgument<"Args">];
3643-
let LateParsed = 1;
3686+
let LateParsed = LateAttrParseStandard;
36443687
let TemplateDependent = 1;
36453688
let ParseArgumentsAsUnevaluated = 1;
36463689
let InheritEvenIfAlreadyPresent = 1;
@@ -3651,7 +3694,7 @@ def SharedTrylockFunction : InheritableAttr {
36513694
def LockReturned : InheritableAttr {
36523695
let Spellings = [GNU<"lock_returned">];
36533696
let Args = [ExprArgument<"Arg">];
3654-
let LateParsed = 1;
3697+
let LateParsed = LateAttrParseStandard;
36553698
let TemplateDependent = 1;
36563699
let ParseArgumentsAsUnevaluated = 1;
36573700
let Subjects = SubjectList<[Function]>;
@@ -3661,7 +3704,7 @@ def LockReturned : InheritableAttr {
36613704
def LocksExcluded : InheritableAttr {
36623705
let Spellings = [GNU<"locks_excluded">];
36633706
let Args = [VariadicExprArgument<"Args">];
3664-
let LateParsed = 1;
3707+
let LateParsed = LateAttrParseStandard;
36653708
let TemplateDependent = 1;
36663709
let ParseArgumentsAsUnevaluated = 1;
36673710
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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,12 +1373,21 @@ class Parser : public CodeCompletionHandler {
13731373
// A list of late-parsed attributes. Used by ParseGNUAttributes.
13741374
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: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,23 @@ 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 llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
97+
#include "clang/Parse/AttrParserStringSwitches.inc"
98+
.Default(false);
99+
#undef CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST
100+
}
101+
102+
/// returns true iff attribute is annotated with `LateAttrParseStandard` in
103+
/// `Attr.td`.
104+
static bool IsAttributeLateParsedStandard(const IdentifierInfo &II) {
95105
#define CLANG_ATTR_LATE_PARSED_LIST
96-
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
106+
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
97107
#include "clang/Parse/AttrParserStringSwitches.inc"
98-
.Default(false);
108+
.Default(false);
99109
#undef CLANG_ATTR_LATE_PARSED_LIST
100110
}
101111

@@ -220,8 +230,26 @@ void Parser::ParseGNUAttributes(ParsedAttributes &Attrs,
220230
continue;
221231
}
222232

233+
bool LateParse = false;
234+
if (!LateAttrs)
235+
LateParse = false;
236+
else if (LateAttrs->lateAttrParseExperimentalExtOnly()) {
237+
// The caller requested that this attribute **only** be late
238+
// parsed for `LateAttrParseExperimentalExt` attributes. This will
239+
// only be late parsed if the experimental language option is enabled.
240+
LateParse = getLangOpts().ExperimentalLateParseAttributes &&
241+
IsAttributeLateParsedExperimentalExt(*AttrName);
242+
} else {
243+
// The caller did not restrict late parsing to only
244+
// `LateAttrParseExperimentalExt` attributes so late parse
245+
// both `LateAttrParseStandard` and `LateAttrParseExperimentalExt`
246+
// attributes.
247+
LateParse = IsAttributeLateParsedExperimentalExt(*AttrName) ||
248+
IsAttributeLateParsedStandard(*AttrName);
249+
}
250+
223251
// Handle "parameterized" attributes
224-
if (!LateAttrs || !isAttributeLateParsed(*AttrName)) {
252+
if (!LateParse) {
225253
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, &EndLoc, nullptr,
226254
SourceLocation(), ParsedAttr::Form::GNU(), D);
227255
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)