Skip to content

Commit b1867e1

Browse files
authored
[Attributes] Support Attributes being declared as supporting an experimental late parsing mode "extension" (#88596)
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 65ee8f1 commit b1867e1

File tree

9 files changed

+254
-42
lines changed

9 files changed

+254
-42
lines changed

clang/docs/ReleaseNotes.rst

+4
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ New Compiler Flags
256256
- ``-fexperimental-modules-reduced-bmi`` enables the Reduced BMI for C++20 named modules.
257257
See the document of standard C++ modules for details.
258258

259+
- ``-fexperimental-late-parse-attributes`` enables an experimental feature to
260+
allow late parsing certain attributes in specific contexts where they would
261+
not normally be late parsed.
262+
259263
Deprecated Compiler Flags
260264
-------------------------
261265

clang/include/clang/Basic/Attr.td

+61-18
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

+1
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

+7
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,13 @@ defm double_square_bracket_attributes : BoolFOption<"double-square-bracket-attri
16081608
LangOpts<"DoubleSquareBracketAttributes">, DefaultTrue, PosFlag<SetTrue>,
16091609
NegFlag<SetFalse>>;
16101610

1611+
defm experimental_late_parse_attributes : BoolFOption<"experimental-late-parse-attributes",
1612+
LangOpts<"ExperimentalLateParseAttributes">, DefaultFalse,
1613+
PosFlag<SetTrue, [], [ClangOption], "Enable">,
1614+
NegFlag<SetFalse, [], [ClangOption], "Disable">,
1615+
BothFlags<[], [ClangOption, CC1Option],
1616+
" experimental late parsing of attributes">>;
1617+
16111618
defm autolink : BoolFOption<"autolink",
16121619
CodeGenOpts<"Autolink">, DefaultTrue,
16131620
NegFlag<SetFalse, [], [ClangOption, CC1Option],

clang/include/clang/Parse/Parser.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -1398,12 +1398,21 @@ class Parser : public CodeCompletionHandler {
13981398
// A list of late-parsed attributes. Used by ParseGNUAttributes.
13991399
class LateParsedAttrList: public SmallVector<LateParsedAttribute *, 2> {
14001400
public:
1401-
LateParsedAttrList(bool PSoon = false) : ParseSoon(PSoon) { }
1401+
LateParsedAttrList(bool PSoon = false,
1402+
bool LateAttrParseExperimentalExtOnly = false)
1403+
: ParseSoon(PSoon),
1404+
LateAttrParseExperimentalExtOnly(LateAttrParseExperimentalExtOnly) {}
14021405

14031406
bool parseSoon() { return ParseSoon; }
1407+
/// returns true iff the attribute to be parsed should only be late parsed
1408+
/// if it is annotated with `LateAttrParseExperimentalExt`
1409+
bool lateAttrParseExperimentalExtOnly() {
1410+
return LateAttrParseExperimentalExtOnly;
1411+
}
14041412

14051413
private:
1406-
bool ParseSoon; // Are we planning to parse these shortly after creation?
1414+
bool ParseSoon; // Are we planning to parse these shortly after creation?
1415+
bool LateAttrParseExperimentalExtOnly;
14071416
};
14081417

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

clang/lib/Driver/ToolChains/Clang.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -7490,6 +7490,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
74907490
Args.addOptInFlag(CmdArgs, options::OPT_fsafe_buffer_usage_suggestions,
74917491
options::OPT_fno_safe_buffer_usage_suggestions);
74927492

7493+
Args.addOptInFlag(CmdArgs, options::OPT_fexperimental_late_parse_attributes,
7494+
options::OPT_fno_experimental_late_parse_attributes);
7495+
74937496
// Setup statistics file output.
74947497
SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D);
74957498
if (!StatsFile.empty()) {

clang/lib/Parse/ParseDecl.cpp

+34-6
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,23 @@ static StringRef normalizeAttrName(StringRef Name) {
9191
return Name;
9292
}
9393

94-
/// isAttributeLateParsed - Return true if the attribute has arguments that
95-
/// require late parsing.
96-
static bool isAttributeLateParsed(const IdentifierInfo &II) {
94+
/// returns true iff attribute is annotated with `LateAttrParseExperimentalExt`
95+
/// in `Attr.td`.
96+
static bool IsAttributeLateParsedExperimentalExt(const IdentifierInfo &II) {
97+
#define CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST
98+
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
99+
#include "clang/Parse/AttrParserStringSwitches.inc"
100+
.Default(false);
101+
#undef CLANG_ATTR_LATE_PARSED_EXPERIMENTAL_EXT_LIST
102+
}
103+
104+
/// returns true iff attribute is annotated with `LateAttrParseStandard` in
105+
/// `Attr.td`.
106+
static bool IsAttributeLateParsedStandard(const IdentifierInfo &II) {
97107
#define CLANG_ATTR_LATE_PARSED_LIST
98-
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
108+
return llvm::StringSwitch<bool>(normalizeAttrName(II.getName()))
99109
#include "clang/Parse/AttrParserStringSwitches.inc"
100-
.Default(false);
110+
.Default(false);
101111
#undef CLANG_ATTR_LATE_PARSED_LIST
102112
}
103113

@@ -222,8 +232,26 @@ void Parser::ParseGNUAttributes(ParsedAttributes &Attrs,
222232
continue;
223233
}
224234

235+
bool LateParse = false;
236+
if (!LateAttrs)
237+
LateParse = false;
238+
else 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 = getLangOpts().ExperimentalLateParseAttributes &&
243+
IsAttributeLateParsedExperimentalExt(*AttrName);
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+
225253
// Handle "parameterized" attributes
226-
if (!LateAttrs || !isAttributeLateParsed(*AttrName)) {
254+
if (!LateParse) {
227255
ParseGNUAttributeArgs(AttrName, AttrNameLoc, Attrs, &EndLoc, nullptr,
228256
SourceLocation(), ParsedAttr::Form::GNU(), D);
229257
continue;
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)