diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c43f236aab319..6df6e1d46d3ac 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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``, diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index aac8c1f550cb2..d1847754f6329 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -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]>; diff --git a/clang/include/clang/Basic/AttributeCommonInfo.h b/clang/include/clang/Basic/AttributeCommonInfo.h index 77b5eb8a1a7cc..4825da7d7f1b1 100644 --- a/clang/include/clang/Basic/AttributeCommonInfo.h +++ b/clang/include/clang/Basic/AttributeCommonInfo.h @@ -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 { @@ -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) @@ -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!"); } @@ -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(); } @@ -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 { diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index aa0ccb0c05101..adbd6c55883ea 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -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, DefaultIgnore; +def err_cxx26_compat_annotation : ExtWarn< + "annotations are a C++26 extension">, InGroup, 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; @@ -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, DefaultIgnore; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3e864475f22a1..423dbfdb5a557 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index dad8efd0f017f..9ca8b69265878 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -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 diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 43a48c92fc305..a5d1f155605e4 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -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"); } diff --git a/clang/include/clang/Sema/ParsedAttr.h b/clang/include/clang/Sema/ParsedAttr.h index 5387f9fad6cd2..92659e1dafd9f 100644 --- a/clang/include/clang/Sema/ParsedAttr.h +++ b/clang/include/clang/Sema/ParsedAttr.h @@ -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, @@ -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; @@ -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(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, @@ -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, diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index b96968d4592f5..3e922eeb6273f 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -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" @@ -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, @@ -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); @@ -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 @@ -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)) diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp index 2b5ad33ad7b29..2dd432b8943ba 100644 --- a/clang/lib/Sema/ParsedAttr.cpp +++ b/clang/lib/Sema/ParsedAttr.cpp @@ -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); @@ -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; } @@ -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()); @@ -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 diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index a9e7b44ac9d73..792be8f5e01f9 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6409,6 +6409,66 @@ static void handleRequiresCapabilityAttr(Sema &S, Decl *D, D->addAttr(RCA); } +static void handleCxx26AnnotationAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + Expr *CE = AL.getArgAsExpr(0); + // Let E be the expression std​::​meta​::​reflect_constant(CE). E + // shall be a constant expression; the result of E is the underlying constant + // of the annotation. + if (CE->isLValue()) { + if (CE->getType()->isRecordType()) { + InitializedEntity Entity = InitializedEntity::InitializeTemporary( + CE->getType().getUnqualifiedType()); + InitializationKind Kind = + InitializationKind::CreateCopy(CE->getExprLoc(), SourceLocation()); + InitializationSequence Seq(S, Entity, Kind, CE); + + ExprResult CopyResult = Seq.Perform(S, Entity, Kind, CE); + if (CopyResult.isInvalid()) + return; + + CE = CopyResult.get(); + } else { + ExprResult RVExprResult = S.DefaultLvalueConversion(AL.getArgAsExpr(0)); + assert(!RVExprResult.isInvalid() && RVExprResult.get()); + + CE = RVExprResult.get(); + } + } + + Expr::EvalResult Result; + + SmallVector Notes; + Result.Diag = &Notes; + + if (!CE->isValueDependent()) { + ConstantExprKind CEKind = + (CE->getType()->isClassType() + ? ConstantExprKind::ClassTemplateArgument + : ConstantExprKind::NonClassTemplateArgument); + + // Argument to annotation must be usable as template argument. + if (!CE->EvaluateAsConstantExpr(Result, S.Context, CEKind)) { + S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type) + << "C++26 annotation" << /*template arg=*/4 << CE->getSourceRange(); + for (auto P : Notes) + S.Diag(P.first, P.second); + + return; + } + // Argument to annotation must evaluate to structural type. + if (!CE->getType()->isStructuralType()) { + S.Diag(CE->getBeginLoc(), diag::err_attribute_argument_type) + << "C++26 annotation" << /*value or structural type*/ 5 + << CE->getSourceRange(); + return; + } + } + auto *Annot = CXX26AnnotationAttr::Create(S.Context, CE, AL); + Annot->setValue(Result.Val); + Annot->setEqLoc(AL.getLoc()); + D->addAttr(Annot); +} + static void handleDeprecatedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (const auto *NSD = dyn_cast(D)) { if (NSD->isAnonymousNamespace()) { @@ -7179,6 +7239,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_Constructor: handleConstructorAttr(S, D, AL); break; + case ParsedAttr::AT_CXX26Annotation: + handleCxx26AnnotationAttr(S, D, AL); + break; case ParsedAttr::AT_Deprecated: handleDeprecatedAttr(S, D, AL); break; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 4d58f00168298..3de4e8fbb2677 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -149,6 +149,26 @@ static void instantiateDependentAlignedAttr( } } +static void instantiateCxx26AnnotationAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const CXX26AnnotationAttr *CXX26AnnotationAttr, Decl *New) { + Expr *CE = CXX26AnnotationAttr->getArg(); + ExprResult Result = S.SubstExpr(CE, TemplateArgs); + if (Result.isInvalid()) + return; + + Expr *concreteExpr = Result.get(); + Expr::EvalResult V; + if (!concreteExpr->EvaluateAsRValue(V, S.getASTContext(), true)) + llvm_unreachable("failed to evaluate annotation expression"); + + auto *Annot = + CXX26AnnotationAttr::Create(S.Context, CE, *CXX26AnnotationAttr); + Annot->setEqLoc(CXX26AnnotationAttr->getEqLoc()); + Annot->setValue(V.Val); + New->addAttr(Annot); +} + static void instantiateDependentAssumeAlignedAttr( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const AssumeAlignedAttr *Aligned, Decl *New) { @@ -856,6 +876,11 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, continue; } + if (const auto *Cxx26Annotation = dyn_cast(TmplAttr)) { + instantiateCxx26AnnotationAttr(*this, TemplateArgs, Cxx26Annotation, New); + continue; + } + if (const auto *AssumeAligned = dyn_cast(TmplAttr)) { instantiateDependentAssumeAlignedAttr(*this, TemplateArgs, AssumeAligned, New); continue; diff --git a/clang/test/SemaCXX/cxx26-annotation.cpp b/clang/test/SemaCXX/cxx26-annotation.cpp new file mode 100644 index 0000000000000..e76d02ff89f6e --- /dev/null +++ b/clang/test/SemaCXX/cxx26-annotation.cpp @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -std=c++26 -fexperimental-new-constant-interpreter -x c++ %s -verify + +struct F { + bool V; +}; +// Nominal cases +// Type +struct [[=1]] f1 {}; +struct [[=1, =F{true}]] f2 {}; +struct [[=1]] [[=2]] f3 {}; +// Declaration +const [[=1]] F f4{}; +void f41([[=F{false}]]int i) {} // function parameters +template [[=3]] void f42(T t); // non dep on template decl +// Redeclaration +[[=2, =3, =2]] void f5(); +void f5 [[=4, =2]] (); + +// Error case +// Mixing annotation and attributes, with or without trailing characters +struct [[nodiscard, =1]] f6 {}; // expected-error {{attribute specifier cannot contain both attributes and annotations}} +struct [[nodiscard, =1,]] f7 {}; // expected-error {{attribute specifier cannot contain both attributes and annotations}} +struct [[=1, nodiscard, ]] f8 {}; // expected-error {{attribute specifier cannot contain both attributes and annotations}} +struct [[=1, nodiscard ]] f9 {}; // expected-error {{attribute specifier cannot contain both attributes and annotations}} +// Mixing attribute using and annotation +struct G { + [[using CC: =1]] [[=2]] int f; // expected-error {{annotations are not permitted following an attribute-using-prefix}} +}; +// Substituting into an annotation is not in the immediate context +template + [[=T::type()]] void h(T t); // expected-error {{type 'char' cannot be used prior to '::' because it has no members}} + // expected-note@#inst-H {{in instantiation of function template specialization 'h' requested here}} +struct T { + static constexpr int type() { return 0; } +}; + +void h(int); +void hh() { + h(0); + h('0'); // #inst-H + h(T{}); +} + +// Handle copying lvalue +struct U { + bool V; + constexpr U(bool v) : V(v) {} + U(const U&) = delete; // #del-U +}; +constexpr U u(true); +struct [[ =u ]] h2{}; // expected-error {{call to deleted constructor of 'U'}} + // expected-note@#del-U {{'U' has been explicitly marked deleted here}} + +// Non structural +struct [[="notstructural"]] h3{}; // expected-error {{C++26 annotation attribute requires an expression usable as a template argument}} \ + expected-note {{reference to string literal is not allowed in a template argument}} + +// Pointer into string literal +struct [[=&"foo"[0]]] h4{}; // expected-error {{C++26 annotation attribute requires an expression usable as a template argument}} \ + expected-note {{pointer to subobject of string literal is not allowed in a template argument}} diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp index 183952af590e1..2ce70b51a7e5e 100644 --- a/clang/utils/TableGen/ClangAttrEmitter.cpp +++ b/clang/utils/TableGen/ClangAttrEmitter.cpp @@ -3860,6 +3860,13 @@ void EmitClangAttrHasAttrImpl(const RecordKeeper &Records, raw_ostream &OS) { Pragma.emplace_back(R, SI); else if (Variety == "HLSLAnnotation") HLSLAnnotation.emplace_back(R, SI); + else if (Variety == "AS_Annotation") { + // We should not be code gening anything with a C++26 + // annotation syntax. + PrintError(R->getLoc(), + "Invalid syntax 'Annotation' used on the node '" + + R->getName() + "'"); + } } } @@ -3904,6 +3911,10 @@ void EmitClangAttrHasAttrImpl(const RecordKeeper &Records, raw_ostream &OS) { OS << " llvm_unreachable (\"hasAttribute not supported for " "AS_Implicit\");\n"; OS << " return 0;\n"; + OS << "case AttributeCommonInfo::Syntax::AS_Annotation:\n"; + OS << " llvm_unreachable (\"hasAttribute not supported for " + "AS_Annotation\");\n"; + OS << " return 0;\n"; OS << "}\n"; }