Skip to content

Commit

Permalink
[clang] Add Parse and Sema support for RegularKeyword attributes
Browse files Browse the repository at this point in the history
This patch adds the Parse and Sema support for RegularKeyword attributes,
following on from a previous patch that added Attr.td support.

The patch is quite large.  However, nothing outside the tests is
specific to the first RegularKeyword attribute (__arm_streaming).
The patch should therefore be a one-off, up-front cost.  Other
attributes just need an entry in Attr.td and the usual Sema support.

The approach taken in the patch is that the keywords can be used with
any language version.  If standard attributes were added in language
version Y, the keyword rules for version X<Y are the same as they were
for version Y (to the extent possible).  Any extensions beyond Y are
handled in the same way for both keywords and attributes.  This ensures
that existing C++11 successors like C++17 are not treated differently
from versions that have yet to be defined.

Some notes on the implementation:

* The patch emits errors rather than warnings for diagnostics that
relate to keywords.

* Where possible, the patch drops “attribute” from diagnostics
relating to keywords.

* One exception to the previous point is that warnings about C++
extensions do still mention attributes.  The use there seemed OK
since the diagnostics are noting a change in the production rules.

* If a diagnostic string needs to be different for keywords and
attributes, the patch standardizes on passing the attribute/
name/token followed by 0 for attributes and 1 for keywords.

* Although the patch updates warn_attribute_wrong_decl_type_str,
warn_attribute_wrong_decl_type, and warn_attribute_wrong_decl_type,
only the error forms of these strings are used for keywords.

* I couldn't trigger the warnings in checkUnusedDeclAttributes,
even for existing attributes.  An assert on the warnings caused
no failures in the testsuite.  I think in practice all standard
attributes would be diagnosed before this.

* The patch drops a call to standardAttributesAllowed in
ParseFunctionDeclarator.  This is because MaybeParseCXX11Attributes
checks the same thing itself, where appropriate.

* The new tests are based on c2x-attributes.c and
cxx0x-attributes.cpp.  The C++ test also incorporates a version of
cxx11-base-spec-attributes.cpp.  The FIXMEs are carried across from
the originals.

Differential Revision: https://reviews.llvm.org/D148702
  • Loading branch information
rsandifo-arm committed May 31, 2023
1 parent 301eb6b commit 33ee5c4
Show file tree
Hide file tree
Showing 25 changed files with 807 additions and 163 deletions.
2 changes: 1 addition & 1 deletion clang/examples/Attribute/Attribute.cpp
Expand Up @@ -43,7 +43,7 @@ struct ExampleAttrInfo : public ParsedAttrInfo {
// This attribute appertains to functions only.
if (!isa<FunctionDecl>(D)) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str)
<< Attr << "functions";
<< Attr << Attr.isRegularKeywordAttribute() << "functions";
return false;
}
return true;
Expand Down
2 changes: 1 addition & 1 deletion clang/examples/CallSuperAttribute/CallSuperAttrInfo.cpp
Expand Up @@ -169,7 +169,7 @@ struct CallSuperAttrInfo : public ParsedAttrInfo {
const auto *TheMethod = dyn_cast_or_null<CXXMethodDecl>(D);
if (!TheMethod || !TheMethod->isVirtual()) {
S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type_str)
<< Attr << "virtual functions";
<< Attr << Attr.isRegularKeywordAttribute() << "virtual functions";
return false;
}
MarkedMethods.insert(TheMethod);
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/Basic/DiagnosticCommonKinds.td
Expand Up @@ -121,7 +121,7 @@ def note_pragma_entered_here : Note<"#pragma entered here">;
def note_decl_hiding_tag_type : Note<
"%1 %0 is hidden by a non-type declaration of %0 here">;
def err_attribute_not_type_attr : Error<
"%0 attribute cannot be applied to types">;
"%0%select{ attribute|}1 cannot be applied to types">;
def err_enum_template : Error<"enumeration cannot be a template">;

def warn_cxx20_compat_consteval : Warning<
Expand Down Expand Up @@ -175,6 +175,8 @@ def warn_unknown_attribute_ignored : Warning<
"unknown attribute %0 ignored">, InGroup<UnknownAttributes>;
def warn_attribute_ignored : Warning<"%0 attribute ignored">,
InGroup<IgnoredAttributes>;
def err_keyword_not_supported_on_target : Error<
"%0 is not supported on this target">;
def err_use_of_tag_name_without_tag : Error<
"must use '%1' tag to refer to type %0%select{| in this scope}2">;

Expand Down
17 changes: 12 additions & 5 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Expand Up @@ -733,10 +733,12 @@ def ext_using_attribute_ns : ExtWarn<
def err_using_attribute_ns_conflict : Error<
"attribute with scope specifier cannot follow default scope specifier">;
def err_attributes_not_allowed : Error<"an attribute list cannot appear here">;
def err_keyword_not_allowed : Error<"%0 cannot appear here">;
def ext_cxx11_attr_placement : ExtWarn<
"ISO C++ does not allow an attribute list to appear here">,
"ISO C++ does not allow %select{an attribute list|%0}1 to appear here">,
InGroup<DiagGroup<"cxx-attribute-extension">>;
def err_attributes_misplaced : Error<"misplaced attributes; expected attributes here">;
def err_keyword_misplaced : Error<"misplaced %0; expected %0 here">;
def err_l_square_l_square_not_attribute : Error<
"C++11 only allows consecutive left square brackets when "
"introducing an attribute">;
Expand Down Expand Up @@ -1014,14 +1016,15 @@ def err_lambda_capture_multiple_ellipses : Error<
def err_capture_default_first : Error<
"capture default must be first">;
def ext_decl_attrs_on_lambda : ExtWarn<
"an attribute specifier sequence in this position is a C++23 extension">,
InGroup<CXX23>;
"%select{an attribute specifier sequence|%0}1 in this position "
"is a C++23 extension">, InGroup<CXX23>;
def ext_lambda_missing_parens : ExtWarn<
"lambda without a parameter clause is a C++23 extension">,
InGroup<CXX23>;
def warn_cxx20_compat_decl_attrs_on_lambda : Warning<
"an attribute specifier sequence in this position is incompatible with C++ "
"standards before C++23">, InGroup<CXXPre23Compat>, DefaultIgnore;
"%select{an attribute specifier sequence|%1}0 in this position "
"is incompatible with C++ standards before C++23">,
InGroup<CXXPre23Compat>, DefaultIgnore;

// C++17 lambda expressions
def err_expected_star_this_capture : Error<
Expand Down Expand Up @@ -1582,8 +1585,12 @@ def err_module_expected_ident : Error<
"expected a module name after '%select{module|import}0'">;
def err_attribute_not_module_attr : Error<
"%0 attribute cannot be applied to a module">;
def err_keyword_not_module_attr : Error<
"%0 cannot be applied to a module">;
def err_attribute_not_import_attr : Error<
"%0 attribute cannot be applied to a module import">;
def err_keyword_not_import_attr : Error<
"%0 cannot be applied to a module import">;
def err_module_expected_semi : Error<
"expected ';' after module name">;
def err_global_module_introducer_not_at_start : Error<
Expand Down
20 changes: 13 additions & 7 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -3022,7 +3022,7 @@ def err_musttail_no_variadic : Error<
def err_nsobject_attribute : Error<
"'NSObject' attribute is for pointer types only">;
def err_attributes_are_not_compatible : Error<
"%0 and %1 attributes are not compatible">;
"%0 and %1%select{ attributes|}2 are not compatible">;
def err_attribute_invalid_argument : Error<
"%select{a reference type|an array type|a non-vector or "
"non-vectorizable scalar type}0 is an invalid argument to attribute %1">;
Expand Down Expand Up @@ -3430,16 +3430,20 @@ def warn_attribute_has_no_effect_on_compile_time_if : Warning<
def note_attribute_has_no_effect_on_compile_time_if_here : Note<
"annotating the 'if %select{constexpr|consteval}0' statement here">;
def err_decl_attribute_invalid_on_stmt : Error<
"%0 attribute cannot be applied to a statement">;
"%0%select{ attribute|}1 cannot be applied to a statement">;
def err_attribute_invalid_on_decl : Error<
"%0 attribute cannot be applied to a declaration">;
"%0%select{ attribute|}1 cannot be applied to a declaration">;
def warn_type_attribute_deprecated_on_decl : Warning<
"applying attribute %0 to a declaration is deprecated; apply it to the type instead">,
InGroup<DeprecatedAttributes>;
def warn_declspec_attribute_ignored : Warning<
"attribute %0 is ignored, place it after "
"\"%select{class|struct|interface|union|enum|enum class|enum struct}1\" to apply attribute to "
"type declaration">, InGroup<IgnoredAttributes>;
def err_declspec_keyword_has_no_effect : Error<
"%0 cannot appear here, place it after "
"\"%select{class|struct|interface|union|enum}1\" to apply it to the "
"type declaration">;
def warn_attribute_precede_definition : Warning<
"attribute declaration must precede definition">,
InGroup<IgnoredAttributes>;
Expand Down Expand Up @@ -3538,11 +3542,11 @@ def err_attribute_weakref_without_alias : Error<
def err_alias_not_supported_on_darwin : Error <
"aliases are not supported on darwin">;
def warn_attribute_wrong_decl_type_str : Warning<
"%0 attribute only applies to %1">, InGroup<IgnoredAttributes>;
"%0%select{ attribute|}1 only applies to %2">, InGroup<IgnoredAttributes>;
def err_attribute_wrong_decl_type_str : Error<
warn_attribute_wrong_decl_type_str.Summary>;
def warn_attribute_wrong_decl_type : Warning<
"%0 attribute only applies to %select{"
"%0%select{ attribute|}1 only applies to %select{"
"functions"
"|unions"
"|variables and functions"
Expand All @@ -3555,13 +3559,15 @@ def warn_attribute_wrong_decl_type : Warning<
"|types and namespaces"
"|variables, functions and classes"
"|kernel functions"
"|non-K&R-style functions}1">,
"|non-K&R-style functions}2">,
InGroup<IgnoredAttributes>;
def err_attribute_wrong_decl_type : Error<warn_attribute_wrong_decl_type.Summary>;
def warn_type_attribute_wrong_type : Warning<
"'%0' only applies to %select{function|pointer|"
"Objective-C object or block pointer}1 types; type here is %2">,
InGroup<IgnoredAttributes>;
def err_type_attribute_wrong_type : Error<
warn_type_attribute_wrong_type.Summary>;
def warn_incomplete_encoded_type : Warning<
"encoding of %0 type is incomplete because %1 component has unknown encoding">,
InGroup<DiagGroup<"encode-type">>;
Expand Down Expand Up @@ -3612,7 +3618,7 @@ def err_invalid_pcs : Error<"invalid PCS type">;
def warn_attribute_not_on_decl : Warning<
"%0 attribute ignored when parsing type">, InGroup<IgnoredAttributes>;
def err_base_specifier_attribute : Error<
"%0 attribute cannot be applied to a base specifier">;
"%0%select{ attribute|}1 cannot be applied to a base specifier">;
def warn_declspec_allocator_nonpointer : Warning<
"ignoring __declspec(allocator) because the function return type %0 is not "
"a pointer or reference type">, InGroup<IgnoredAttributes>;
Expand Down
40 changes: 27 additions & 13 deletions clang/include/clang/Parse/Parser.h
Expand Up @@ -2691,6 +2691,18 @@ class Parser : public CodeCompletionHandler {
return LO.DoubleSquareBracketAttributes;
}

/// Return true if the next token should be treated as a [[]] attribute,
/// or as a keyword that behaves like one. The former is only true if
/// [[]] attributes are enabled, whereas the latter is true whenever
/// such a keyword appears. The arguments are as for
/// isCXX11AttributeSpecifier.
bool isAllowedCXX11AttributeSpecifier(bool Disambiguate = false,
bool OuterMightBeMessageSend = false) {
return (Tok.isRegularKeywordAttribute() ||
(standardAttributesAllowed() &&
isCXX11AttributeSpecifier(Disambiguate, OuterMightBeMessageSend)));
}

// Check for the start of an attribute-specifier-seq in a context where an
// attribute is not allowed.
bool CheckProhibitedCXX11Attribute() {
Expand All @@ -2703,11 +2715,13 @@ class Parser : public CodeCompletionHandler {
bool DiagnoseProhibitedCXX11Attribute();
void CheckMisplacedCXX11Attribute(ParsedAttributes &Attrs,
SourceLocation CorrectLocation) {
if (!standardAttributesAllowed())
return;
if ((Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) &&
Tok.isNot(tok::kw_alignas))
return;
if (!Tok.isRegularKeywordAttribute()) {
if (!standardAttributesAllowed())
return;
if ((Tok.isNot(tok::l_square) || NextToken().isNot(tok::l_square)) &&
Tok.isNot(tok::kw_alignas))
return;
}
DiagnoseMisplacedCXX11Attribute(Attrs, CorrectLocation);
}
void DiagnoseMisplacedCXX11Attribute(ParsedAttributes &Attrs,
Expand All @@ -2721,18 +2735,18 @@ class Parser : public CodeCompletionHandler {
SourceLocation FixItLoc = SourceLocation()) {
if (Attrs.Range.isInvalid())
return;
DiagnoseProhibitedAttributes(Attrs.Range, FixItLoc);
DiagnoseProhibitedAttributes(Attrs, FixItLoc);
Attrs.clear();
}

void ProhibitAttributes(ParsedAttributesView &Attrs,
SourceLocation FixItLoc = SourceLocation()) {
if (Attrs.Range.isInvalid())
return;
DiagnoseProhibitedAttributes(Attrs.Range, FixItLoc);
DiagnoseProhibitedAttributes(Attrs, FixItLoc);
Attrs.clearListOnly();
}
void DiagnoseProhibitedAttributes(const SourceRange &Range,
void DiagnoseProhibitedAttributes(const ParsedAttributesView &Attrs,
SourceLocation FixItLoc);

// Forbid C++11 and C2x attributes that appear on certain syntactic locations
Expand All @@ -2741,7 +2755,8 @@ class Parser : public CodeCompletionHandler {
// For the most cases we don't want to warn on unknown type attributes, but
// left them to later diagnoses. However, for a few cases like module
// declarations and module import declarations, we should do it.
void ProhibitCXX11Attributes(ParsedAttributes &Attrs, unsigned DiagID,
void ProhibitCXX11Attributes(ParsedAttributes &Attrs, unsigned AttrDiagID,
unsigned KeywordDiagId,
bool DiagnoseEmptyAttrs = false,
bool WarnOnUnknownAttrs = false);

Expand Down Expand Up @@ -2795,7 +2810,7 @@ class Parser : public CodeCompletionHandler {
bool MaybeParseAttributes(unsigned WhichAttrKinds, ParsedAttributes &Attrs,
LateParsedAttrList *LateAttrs = nullptr) {
if (Tok.isOneOf(tok::kw___attribute, tok::kw___declspec) ||
(standardAttributesAllowed() && isCXX11AttributeSpecifier())) {
isAllowedCXX11AttributeSpecifier()) {
ParseAttributes(WhichAttrKinds, Attrs, LateAttrs);
return true;
}
Expand Down Expand Up @@ -2847,7 +2862,7 @@ class Parser : public CodeCompletionHandler {
}
}
void MaybeParseCXX11Attributes(Declarator &D) {
if (standardAttributesAllowed() && isCXX11AttributeSpecifier()) {
if (isAllowedCXX11AttributeSpecifier()) {
ParsedAttributes Attrs(AttrFactory);
ParseCXX11Attributes(Attrs);
D.takeAttributes(Attrs);
Expand All @@ -2856,8 +2871,7 @@ class Parser : public CodeCompletionHandler {

bool MaybeParseCXX11Attributes(ParsedAttributes &Attrs,
bool OuterMightBeMessageSend = false) {
if (standardAttributesAllowed() &&
isCXX11AttributeSpecifier(false, OuterMightBeMessageSend)) {
if (isAllowedCXX11AttributeSpecifier(false, OuterMightBeMessageSend)) {
ParseCXX11Attributes(Attrs);
return true;
}
Expand Down
13 changes: 3 additions & 10 deletions clang/include/clang/Sema/DeclSpec.h
Expand Up @@ -1972,9 +1972,10 @@ class Declarator {
InventedTemplateParameterList(nullptr) {
assert(llvm::all_of(DeclarationAttrs,
[](const ParsedAttr &AL) {
return AL.isStandardAttributeSyntax();
return (AL.isStandardAttributeSyntax() ||
AL.isRegularKeywordAttribute());
}) &&
"DeclarationAttrs may only contain [[]] attributes");
"DeclarationAttrs may only contain [[]] and keyword attributes");
}

~Declarator() {
Expand Down Expand Up @@ -2619,14 +2620,6 @@ class Declarator {
return false;
}

/// Return a source range list of C++11 attributes associated
/// with the declarator.
void getCXX11AttributeRanges(SmallVectorImpl<SourceRange> &Ranges) {
for (const ParsedAttr &AL : Attrs)
if (AL.isCXX11Attribute())
Ranges.push_back(AL.getRange());
}

void setAsmLabel(Expr *E) { AsmLabel = E; }
Expr *getAsmLabel() const { return AsmLabel; }

Expand Down

0 comments on commit 33ee5c4

Please sign in to comment.