Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ Attribute Changes in Clang
- New format attributes ``gnu_printf``, ``gnu_scanf``, ``gnu_strftime`` and ``gnu_strfmon`` are added
as aliases for ``printf``, ``scanf``, ``strftime`` and ``strfmon``. (#GH16219)

- Annotations from C++26 are supported (still experimental), and gated under the `-freflection` flag.

Improvements to Clang's diagnostics
-----------------------------------
- Diagnostics messages now refer to ``structured binding`` instead of ``decomposition``,
Expand Down
23 changes: 23 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,29 @@ def AlwaysInline : DeclOrStmtAttr {
let Documentation = [AlwaysInlineDocs];
}

def CXX26Annotation : InheritableParamAttr {
let Spellings = [];
let Args = [ExprArgument<"Arg">];
let AdditionalMembers = [{
private:
APValue Value;
SourceLocation EqLoc;

public:

const APValue & getValue() const { return Value; }
void setValue(APValue V) { Value = V; }

SourceLocation getEqLoc() const { return EqLoc; }
void setEqLoc(SourceLocation Loc) { EqLoc = Loc; }
}];

let HasCustomParsing = 1;
let TemplateDependent = 1;
let MeaningfulToClassTemplateDefinition = 1;
let Documentation = [InternalOnly];
}

def Artificial : InheritableAttr {
let Spellings = [GCC<"artificial">];
let Subjects = SubjectList<[InlineFunction]>;
Expand Down
11 changes: 9 additions & 2 deletions clang/include/clang/Basic/AttributeCommonInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ class AttributeCommonInfo {

/// The attibute has no source code manifestation and is only created
/// implicitly.
AS_Implicit
AS_Implicit,

/// The attribute is a C++26 annotation.
AS_Annotation,
};

enum Kind {
Expand Down Expand Up @@ -133,6 +136,7 @@ class AttributeCommonInfo {
static Form ContextSensitiveKeyword() { return AS_ContextSensitiveKeyword; }
static Form HLSLAnnotation() { return AS_HLSLAnnotation; }
static Form Implicit() { return AS_Implicit; }
static Form Annotation() { return AS_Annotation; }

private:
constexpr Form(Syntax SyntaxUsed)
Expand All @@ -156,7 +160,7 @@ class AttributeCommonInfo {
SpellingIndex(FormUsed.getSpellingIndex()),
IsAlignas(FormUsed.isAlignas()),
IsRegularKeywordAttribute(FormUsed.isRegularKeywordAttribute()) {
assert(SyntaxUsed >= AS_GNU && SyntaxUsed <= AS_Implicit &&
assert(SyntaxUsed >= AS_GNU && SyntaxUsed <= AS_Annotation &&
"Invalid syntax!");
}

Expand Down Expand Up @@ -189,6 +193,7 @@ class AttributeCommonInfo {
return Form(getSyntax(), SpellingIndex, IsAlignas,
IsRegularKeywordAttribute);
}

const IdentifierInfo *getAttrName() const { return AttrName; }
void setAttrName(const IdentifierInfo *AttrNameII) { AttrName = AttrNameII; }
SourceLocation getLoc() const { return AttrRange.getBegin(); }
Expand Down Expand Up @@ -226,6 +231,8 @@ class AttributeCommonInfo {

bool isCXX11Attribute() const { return SyntaxUsed == AS_CXX11 || IsAlignas; }

bool isCXX26Annotation() const { return SyntaxUsed == AS_Annotation; }

bool isC23Attribute() const { return SyntaxUsed == AS_C23; }

bool isAlignas() const {
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,10 @@ def err_cxx11_attribute_forbids_ellipsis : Error<
def warn_cxx14_compat_using_attribute_ns : Warning<
"default scope specifier for attributes is incompatible with C++ standards "
"before C++17">, InGroup<CXXPre17Compat>, DefaultIgnore;
def err_cxx26_compat_annotation : ExtWarn<
"annotations are a C++26 extension">, InGroup<CXXPre26Compat>, DefaultError;
def err_mixed_attributes_and_annotations : Error<
"attribute specifier cannot contain both attributes and annotations">;
def ext_using_attribute_ns : ExtWarn<
"default scope specifier for attributes is a C++17 extension">,
InGroup<CXX17>;
Expand Down Expand Up @@ -1851,6 +1855,11 @@ def err_placeholder_expected_auto_or_decltype_auto : Error<
"expected 'auto' or 'decltype(auto)' after concept name">;
}

let CategoryName = "Reflection Issue" in {
def err_annotation_with_using : Error<
"annotations are not permitted following an attribute-using-prefix">;
}

def warn_max_tokens : Warning<
"the number of preprocessor source tokens (%0) exceeds this token limit (%1)">,
InGroup<MaxTokens>, DefaultIgnore;
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3424,7 +3424,8 @@ def err_attribute_argument_n_type : Error<
"constant|a string|an identifier|a constant expression|a builtin function}2">;
def err_attribute_argument_type : Error<
"%0 attribute requires %select{int or bool|an integer "
"constant|a string|an identifier}1">;
"constant|a string|an identifier|an expression usable as a template argument|"
"a value of structural type}1">;
def err_attribute_argument_out_of_range : Error<
"%0 attribute requires integer constant between %1 and %2 inclusive">;
def err_init_priority_object_attr : Error<
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -3046,6 +3046,10 @@ class Parser : public CodeCompletionHandler {
SourceLocation ScopeLoc,
CachedTokens &OpenMPTokens);

/// Parse an annotation as specified from C++26.
void ParseAnnotationSpecifier(ParsedAttributes &Attrs,
SourceLocation *EndLoc);

/// Parse the argument to C++23's [[assume()]] attribute. Returns true on
/// error.
bool
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/DeclSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -2008,7 +2008,8 @@ class Declarator {
assert(llvm::all_of(DeclarationAttrs,
[](const ParsedAttr &AL) {
return (AL.isStandardAttributeSyntax() ||
AL.isRegularKeywordAttribute());
AL.isRegularKeywordAttribute() ||
AL.isCXX26Annotation());
}) &&
"DeclarationAttrs may only contain [[]] and keyword attributes");
}
Expand Down
41 changes: 41 additions & 0 deletions clang/include/clang/Sema/ParsedAttr.h
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,20 @@ class ParsedAttr final
memcpy(getArgsBuffer(), args, numArgs * sizeof(ArgsUnion));
}

/// Constructor for an annotation with expression argument
ParsedAttr(SourceRange attrRange, AttributeScopeInfo scope, ArgsUnion *args,
unsigned numArgs, Form formUsed, SourceLocation ellipsisLoc)
: AttributeCommonInfo(attrRange, ParsedAttr::Kind::AT_CXX26Annotation,
ParsedAttr::Form::Annotation()),
EllipsisLoc(ellipsisLoc), NumArgs(numArgs), Invalid(false),
UsedAsTypeAttr(false), IsAvailability(false),
IsTypeTagForDatatype(false), IsProperty(false), HasParsedType(false),
HasProcessingCache(false), IsPragmaClangAttribute(false),
Info(ParsedAttrInfo::get(*this)) {
assert(numArgs == 1);
memcpy(getArgsBuffer(), args, sizeof(ArgsUnion));
}

/// Constructor for availability attributes.
ParsedAttr(IdentifierInfo *attrName, SourceRange attrRange,
AttributeScopeInfo scope, IdentifierLoc *Parm,
Expand Down Expand Up @@ -504,6 +518,7 @@ class ParsedAttr final
/// error. Returns false if a diagnostic is produced.
bool checkAtMostNumArgs(class Sema &S, unsigned Num) const;

bool isAnnotationAttr() const;
bool isTargetSpecificAttr() const;
bool isTypeAttr() const;
bool isStmtAttr() const;
Expand Down Expand Up @@ -732,6 +747,22 @@ class AttributePool {
/// them at the end of this \c AttributePool.
void takeFrom(ParsedAttributesView &List, AttributePool &Pool);

// Create a Cxx26 annotation
ParsedAttr *create(SourceRange attrRange, AttributeScopeInfo scope,
ArgsUnion *args, unsigned numArgs, ParsedAttr::Form form,
SourceLocation ellipsisLoc = SourceLocation()) {
// Only annotations are allowed to pass through without an identifier
assert(form.getSyntax() == ParsedAttr::Syntax::AS_Annotation);

void *memory = allocate(
ParsedAttr::totalSizeToAlloc<ArgsUnion, detail::AvailabilityData,
detail::TypeTagForDatatypeData, ParsedType,
detail::PropertyData>(numArgs, 0, 0, 0,
0));
return add(new (memory) ParsedAttr(attrRange, scope, args, numArgs, form,
ellipsisLoc));
}

ParsedAttr *create(IdentifierInfo *attrName, SourceRange attrRange,
AttributeScopeInfo scope, ArgsUnion *args,
unsigned numArgs, ParsedAttr::Form form,
Expand Down Expand Up @@ -974,6 +1005,16 @@ class ParsedAttributes : public ParsedAttributesView {
Range = SourceRange();
}

/// Add annotation with expression argument
ParsedAttr *addNew(SourceRange attrRange, AttributeScopeInfo scope,
ArgsUnion *args, unsigned numArgs, ParsedAttr::Form form,
SourceLocation ellipsisLoc = SourceLocation()) {
ParsedAttr *attr =
pool.create(attrRange, scope, args, numArgs, form, ellipsisLoc);
addAtEnd(attr);
return attr;
}

/// Add attribute with expression arguments.
ParsedAttr *addNew(IdentifierInfo *attrName, SourceRange attrRange,
AttributeScopeInfo scope, ArgsUnion *args,
Expand Down
71 changes: 60 additions & 11 deletions clang/lib/Parse/ParseDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "clang/Basic/Attributes.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/DiagnosticParse.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/TokenKinds.h"
#include "clang/Lex/LiteralSupport.h"
Expand Down Expand Up @@ -4442,6 +4443,23 @@ static bool IsBuiltInOrStandardCXX11Attribute(IdentifierInfo *AttrName,
}
}

void Parser::ParseAnnotationSpecifier(ParsedAttributes &Attrs,
SourceLocation *EndLoc) {
assert(Tok.is(tok::equal) && "not an annotation");
SourceLocation EqLoc = ConsumeToken();

ExprResult AnnotExpr = ParseConstantExpression();
if (AnnotExpr.isInvalid() || AnnotExpr.get()->containsErrors())
return;

ArgsVector ArgExprs;
ArgExprs.push_back(AnnotExpr.get());
Attrs.addNew(EqLoc, {}, ArgExprs.data(), 1, ParsedAttr::Form::Annotation());

if (EndLoc)
*EndLoc = AnnotExpr.get()->getEndLoc();
}

bool Parser::ParseCXXAssumeAttributeArg(
ParsedAttributes &Attrs, IdentifierInfo *AttrName,
SourceLocation AttrNameLoc, IdentifierInfo *ScopeName,
Expand Down Expand Up @@ -4681,25 +4699,51 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
Diag(Tok.getLocation(), diag::err_expected) << tok::colon;
}

bool AttrParsed = false;
bool HasAttribute = false;
bool HasAnnotation = false;
while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof)) {
if (AttrParsed) {
// If we parsed an attribute, a comma is required before parsing any
// additional attributes.
// If we parsed an attribute/annotation, a comma is required before parsing
// any additional ones.
if (HasAttribute || HasAnnotation) {
if (ExpectAndConsume(tok::comma)) {
SkipUntil(tok::r_square, StopAtSemi | StopBeforeMatch);
continue;
}
AttrParsed = false;
}

// Eat all remaining superfluous commas before parsing the next attribute.
// Eat all remaining superfluous commas before parsing the next attribute
// or annotation.
while (TryConsumeToken(tok::comma))
;

SourceLocation ScopeLoc, AttrLoc;
IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr;

// A '=' token marks the beginning of an annotation with the restriction
// - must not be in C++ < 26,
// - must not have seen 'using X::',
// - must not mix with an attribute.
if (Tok.is(tok::equal)) {
if (!getLangOpts().CPlusPlus26) {
Diag(Tok.getLocation(), diag::err_cxx26_compat_annotation);
SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
continue;
}
if (CommonScopeName) {
Diag(Tok.getLocation(), diag::err_annotation_with_using);
SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
continue;
}
if (HasAttribute) {
Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
continue;
}
ParseAnnotationSpecifier(Attrs, EndLoc);
HasAnnotation = true;
continue;
}

AttrName = TryParseCXX11AttributeIdentifier(
AttrLoc, SemaCodeCompletion::AttributeCompletion::Attribute,
CommonScopeName);
Expand Down Expand Up @@ -4732,12 +4776,17 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
}
}

// Parse attribute arguments
if (Tok.is(tok::l_paren))
AttrParsed = ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
// Parse attribute arguments.
HasAttribute = Tok.is(tok::l_paren) &&
ParseCXX11AttributeArgs(AttrName, AttrLoc, Attrs, EndLoc,
ScopeName, ScopeLoc, OpenMPTokens);

if (!AttrParsed) {
if (!HasAttribute) {
if (HasAnnotation) {
Diag(Tok.getLocation(), diag::err_mixed_attributes_and_annotations);
SkipUntil(tok::r_square, tok::colon, StopBeforeMatch);
continue;
}
Attrs.addNew(AttrName,
SourceRange(ScopeLoc.isValid() && CommonScopeLoc.isInvalid()
? ScopeLoc
Expand All @@ -4747,7 +4796,7 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs,
nullptr, 0,
getLangOpts().CPlusPlus ? ParsedAttr::Form::CXX11()
: ParsedAttr::Form::C23());
AttrParsed = true;
HasAttribute = true;
}

if (TryConsumeToken(tok::ellipsis))
Expand Down
29 changes: 26 additions & 3 deletions clang/lib/Sema/ParsedAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,25 @@ const ParsedAttrInfo &ParsedAttrInfo::get(const AttributeCommonInfo &A) {
if ((size_t)A.getParsedKind() < std::size(AttrInfoMap))
return *AttrInfoMap[A.getParsedKind()];

// If this is an annotation then return an appropriate ParsedAttrInfo.
static const ParsedAttrInfo AnnotationAttrInfo(
AttributeCommonInfo::AT_CXX26Annotation, // AttrKind
1, // NumArgs
0, // OptArgs
1, // NumArgMembers
1, // HasCustomParsing
0, // AcceptsExprPack
0, // IsTargetSpecific
1, // IsType
1, // IsStmt
0, // IsKnownToGCC
0, // IsSupportedByPragmaAttribute
{}, // Spellings
nullptr // ArgNames
);
if (A.getSyntax() == AttributeCommonInfo::AS_Annotation)
return AnnotationAttrInfo;

// If this is an ignored attribute then return an appropriate ParsedAttrInfo.
static const ParsedAttrInfo IgnoredParsedAttrInfo(
AttributeCommonInfo::IgnoredAttribute);
Expand Down Expand Up @@ -184,6 +203,10 @@ bool ParsedAttr::isTargetSpecificAttr() const {
return getInfo().IsTargetSpecific;
}

bool ParsedAttr::isAnnotationAttr() const {
return getParsedKind() == AT_CXX26Annotation;
}

bool ParsedAttr::isTypeAttr() const { return getInfo().IsType; }

bool ParsedAttr::isStmtAttr() const { return getInfo().IsStmt; }
Expand All @@ -194,8 +217,8 @@ bool ParsedAttr::existsInTarget(const TargetInfo &Target) const {
// If the attribute has a target-specific spelling, check that it exists.
// Only call this if the attr is not ignored/unknown. For most targets, this
// function just returns true.
bool HasSpelling = K != IgnoredAttribute && K != UnknownAttribute &&
K != NoSemaHandlerAttribute;
bool HasSpelling = !isAnnotationAttr() && K != IgnoredAttribute &&
K != UnknownAttribute && K != NoSemaHandlerAttribute;
bool TargetSpecificSpellingExists =
!HasSpelling ||
getInfo().spellingExistsInTarget(Target, getAttributeSpellingListIndex());
Expand All @@ -215,7 +238,7 @@ bool ParsedAttr::slidesFromDeclToDeclSpecLegacyBehavior() const {
// atributes.
return false;

assert(isStandardAttributeSyntax() || isAlignas());
assert(isStandardAttributeSyntax() || isAlignas() || isCXX26Annotation());

// We have historically allowed some type attributes with standard attribute
// syntax to slide to the decl-specifier-seq, so we have to keep supporting
Expand Down
Loading