diff --git a/P3385.md b/P3385.md new file mode 100644 index 0000000000000..02dadd0b82be3 --- /dev/null +++ b/P3385.md @@ -0,0 +1,72 @@ +# Attributes reflection (P3385) + +## Support + +- Standard attribute +- String literal attribute argument + +## Implementation note + +### Syntactic to semantic attribute + +In different places we need to traverse back to syntactic attributes from semantic ones, after they were attached to constructs. This happen when for example we write + +```cpp +struct [[nodiscard("yep"), deprecated("dont use me")]] Foo {}; +constexpr auto r = ^^ Foo; +enum class [[ [: r :] ]] E { /* stuff */ }; +``` + +At this point `r` expose `Attr`, not `ParsedAtr`. +As a workaround we add a link back to the syntactic form from the semantic one. The backlink is something we make available in the `Attr` class *but* it is not always the case that we set that backlink. + + Why ? The practical use case of traversing back to the syntactic form is (so far) to easily retrieve the arguments, and because we only want to demonstrate support of string arguments for standard attributes, we only set those backlinks in two cases: `[[nodiscard("arg")]]` and `[[deprecated("arg")]]`. + +With that "design", the challenge becomes to keep those syntactic pieces alive longer than they should, and the clutch we went with is this _thing_ + +```cpp +struct AttributeScratchpad { + AttributeFactory factory; + ParsedAttributes attributes; + ArgsVector ArgExprs; + bool argFound; + AttributeScratchpad() : factory(), attributes(factory), ArgExprs(), argFound(false) {} +}; +``` + +The idea is to keep that `AttributeFactory` alive and hopefully the parsed attributes they own arent falling off the map. Then when we create a semantic attribute, we make sure to have a static-ish syntactic version we can refer to, for ex in the handling of `[[nodiscard]]` + +```cpp + // AL is the parsedAttribute + static struct AttributeScratchpad { + AttributeFactory factory; + ParsedAttributes attributes; + ArgsVector ArgExprs; + bool argFound; + AttributeScratchpad() : factory(), attributes(factory), ArgExprs(), argFound(false) {} + } scratchpad; + if (scratchpad.argFound = AL.getNumArgs() != 0; scratchpad.argFound) { + scratchpad.ArgExprs.push_back(AL.getArg(0)); + } else { + scratchpad.ArgExprs.clear(); + } + auto * stashedSyntacticAttribute = scratchpad.attributes.addNew( + const_cast(AL.getAttrName()), + AL.getRange(), + nullptr, + AL.getLoc(), + scratchpad.ArgExprs.data(), scratchpad.argFound, + AL.getForm() + ); + + D->addAttr(::new (S.Context) DeprecatedAttr(S.Context, AL, Str, Replacement), stashedSyntacticAttribute); +``` + +#### Alternative + +The hack above is quite terrible, especially because in the end we're only doing it to get back our args... +Some alternatives are + +- Lift: Lift the `ArgsVector` up into the `Attr` class +- Codegen: Augment the codegen in `ClangAttrEmitter` to offer an implementation for `getArgs()` defined virtually in base `Attr` (Thx Dan). +- condition-a-looza: When we get back an `Attr*` we can do a bunch of `if (WarnUnusedResultAttr::classOf(attr)) { /* cast to WarnUnusedResultAttr*...` then we call the relevant getter `attr->getMessage()` \ No newline at end of file diff --git a/clang/include/clang/AST/APValue.h b/clang/include/clang/AST/APValue.h index 21647ffa5ac5a..f3ff6f3ff2170 100644 --- a/clang/include/clang/AST/APValue.h +++ b/clang/include/clang/AST/APValue.h @@ -40,6 +40,7 @@ template class BasicReaderBase; class Expr; class FieldDecl; class NamespaceDecl; + class ParsedAttr; struct PrintingPolicy; class Type; class ValueDecl; @@ -489,6 +490,10 @@ class APValue { return isReflection() && getReflectionKind() == ReflectionKind::Annotation; } + bool isReflectedAttribute() const { + return isReflection() && getReflectionKind() == ReflectionKind::Attribute; + } + void dump() const; void dump(raw_ostream &OS, const ASTContext &Context) const; @@ -678,6 +683,7 @@ class APValue { CXXBaseSpecifier *getReflectedBaseSpecifier() const; TagDataMemberSpec *getReflectedDataMemberSpec() const; CXX26AnnotationAttr *getReflectedAnnotation() const; + ParsedAttr *getReflectedAttribute() const; void setInt(APSInt I) { assert(isInt() && "Invalid accessor"); diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h index ac44e9fdd7c4e..16f551db97f50 100644 --- a/clang/include/clang/AST/Attr.h +++ b/clang/include/clang/AST/Attr.h @@ -37,6 +37,7 @@ class ASTContext; class AttributeCommonInfo; class FunctionDecl; class OMPTraitInfo; +class ParsedAttr; /// Attr - This represents one attribute. class Attr : public AttributeCommonInfo { @@ -60,6 +61,8 @@ class Attr : public AttributeCommonInfo { LLVM_PREFERRED_TYPE(bool) unsigned InheritEvenIfAlreadyPresent : 1; + const ParsedAttr* Backlink; + void *operator new(size_t bytes) noexcept { llvm_unreachable("Attrs cannot be allocated with regular 'new'."); } @@ -82,7 +85,7 @@ class Attr : public AttributeCommonInfo { attr::Kind AK, bool IsLateParsed) : AttributeCommonInfo(CommonInfo), AttrKind(AK), Inherited(false), IsPackExpansion(false), Implicit(false), IsLateParsed(IsLateParsed), - InheritEvenIfAlreadyPresent(false) {} + InheritEvenIfAlreadyPresent(false), Backlink(nullptr) {} public: attr::Kind getKind() const { return static_cast(AttrKind); } @@ -109,6 +112,16 @@ class Attr : public AttributeCommonInfo { bool isLateParsed() const { return IsLateParsed; } + // Backlink to the syntactic form of the attribute + const ParsedAttr* fromParsedAttr() const { return Backlink; } + + // FIXME p3385, garbage design + void setParsedAttr(const ParsedAttr* parsedAttr) { + // Note that backlink can be null (for delayed splice attribute for ex) + assert(Backlink == nullptr && "backlink should not be reassigned"); + Backlink = parsedAttr; + } + // Pretty print this attribute. void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const; diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 6901ddbb17258..30c1db42e09f3 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -57,6 +57,7 @@ class Module; class NamedDecl; class ObjCContainerDecl; class ObjCMethodDecl; +class ParsedAttr; struct PrintingPolicy; class RecordDecl; class SourceManager; @@ -535,6 +536,8 @@ class alignas(8) Decl { const AttrVec &getAttrs() const; void dropAttrs(); void addAttr(Attr *A); + void addAttr(Attr *A, const ParsedAttr* pA); + using attr_iterator = AttrVec::const_iterator; using attr_range = llvm::iterator_range; diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h index ea12345ea49f5..134c7e49081b3 100644 --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -5321,7 +5321,8 @@ class BuiltinBitCastExpr final }; /// Represents a C++2c reflect expression (P2996). The operand of the expression -/// is either a type, an expression, a template-name, or a namespace. +/// is either a type, an expression, a template-name, an attribute or a +/// namespace. class CXXReflectExpr : public Expr { enum class OperandKind { Unset, @@ -5408,12 +5409,12 @@ class CXXReflectExpr : public Expr { /// reflections (P2996). Arguments vary by function. class CXXMetafunctionExpr : public Expr { public: - // Type of callback provided to executing metafunctinons to help evaluate an + // Type of callback provided to executing metafunctions to help evaluate an // expression in the current constant evaluation context. using EvaluateFn = std::function; - // Type of callback provided to report a diagnistc to the evaluation context. + // Type of callback provided to report a diagnostic to the evaluation context. using DiagnoseFn = std::function; diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 9dd8528c04f47..268c8bafdb6c8 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -2991,6 +2991,7 @@ DEF_TRAVERSE_STMT(CXXReflectExpr, { case ReflectionKind::Namespace: case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: + case ReflectionKind::Attribute: // TODO P3385 ? break; } } diff --git a/clang/include/clang/AST/Reflection.h b/clang/include/clang/AST/Reflection.h index 5838d310e00c5..fde146b47d424 100644 --- a/clang/include/clang/AST/Reflection.h +++ b/clang/include/clang/AST/Reflection.h @@ -48,7 +48,7 @@ enum class ReflectionKind { /// /// Corresponds to an APValue (plus a QualType). Object, - + /// \brief A reflection of a value (i.e., the result of a prvalue). /// /// Corresponds to an APValue (plus a QualType). @@ -102,8 +102,10 @@ enum class ReflectionKind { /// \brief A reflection of an annotation (P2996 ext). Annotation, -}; + /// \brief A reflection of a standard attribute (P3385). + Attribute, +}; /// \brief Representation of a hypothetical data member, which could be used to /// complete an incomplete class definition using the 'std::meta::define_class' diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 69d89d6493ca8..08a1df4158d19 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -3442,6 +3442,13 @@ def ObjCRequiresPropertyDefs : InheritableAttr { let SimpleHandler = 1; } +def DelayedSplice : InheritableAttr { + let Spellings = [Clang<"DelayedSplice">]; + let Args = [ExprArgument<"SpliceExpression">]; + let Subjects = SubjectList<[FunctionLike, Type]>; + let Documentation = [InternalOnly]; +} + def Unused : InheritableAttr { let Spellings = [CXX11<"", "maybe_unused", 201603>, GCC<"unused">, C23<"", "maybe_unused", 202106>]; diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td index 68d0e844780b4..9a8faa3e38aa0 100644 --- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td @@ -105,6 +105,9 @@ def err_fe_reflection_incompatible_with_blocks : Error< def err_fe_parameter_reflection_without_reflection : Error< "cannot specify '-fparameter-reflection' without '-freflection'">, DefaultFatal; +def err_fe_attribute_reflection_without_reflection : Error< + "cannot specify '-fattribute-reflection' without '-freflection'">, + DefaultFatal; def err_fe_dependency_file_requires_MT : Error< "-dependency-file requires at least one -MT or -MQ option">; def err_fe_invalid_plugin_name : Error< diff --git a/clang/include/clang/Basic/DiagnosticMetafnKinds.td b/clang/include/clang/Basic/DiagnosticMetafnKinds.td index 3bd7a0019a666..39150bd411d5e 100644 --- a/clang/include/clang/Basic/DiagnosticMetafnKinds.td +++ b/clang/include/clang/Basic/DiagnosticMetafnKinds.td @@ -18,7 +18,7 @@ def metafn_no_associated_property : Note< "%0 has no %select{type|parent}1">; def metafn_cannot_query_property : Note<"cannot query the " "%select{type|object|value|size|alignment|parameters|return type|" - "annotations}0 of %1">; + "annotations|attributes}0 of %1">; // Ranges of entities. def metafn_cannot_introspect_type : Note< @@ -89,6 +89,11 @@ def metafn_value_not_structural_type : Note< def metafn_result_not_representable : Note< "provided %select{value|object}0 cannot be represented by a reflection">; +def metafn_p3385_trace_execution_checkpoint : Warning< + "(p3385-metafn) trace_execution_checkpoint %0">; +def metafn_p3385_non_standard_attribute : Warning< + "(p3385-metafn) Non standard attribute discovered %0">; + // Class definition. def metafn_name_invalid_identifier : Note< "provided name '%0' is not a valid identifier">; diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index f7a9a698911b2..e716f4b2adb2b 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1746,6 +1746,24 @@ def err_annotation_with_using : Error< "annotations are not permitted following an attribute-using-prefix">; } +let CategoryName = "P3385" in { + +def p3385_trace_attribute_parsed : Warning< + "(p3385-Parse) found attribute to parse following caret operator">; +def p3385_trace_empty_attributes_list : Warning< + "(p3385-Parse) reflection of an empty attribute list">; +def p3385_trace_execution_checkpoint : Warning< + "(p3385-Parse) trace_execution_checkpoint %0">; +def p3385_delayed_splice_attr : Warning< + "(p3385-Sema) Found delayed splice attribute while instantiating %0">; +def p3385_err_attributes_list : Error< + "(p3385-Parse) reflection of attribute list is not supported, found %0 attributes">; +def p3385_err_attribute_splicing_error : Error< + "(p3385-Parse) error while splicing attributes reflection: %0">; +def p3385_err_attribute_splicing_with_using_namespace_error : Error< + "(p3385-Parse) using prefix with splice expression is not supported: %0">; +} + let CategoryName = "Generics Issue" in { def err_objc_expected_type_parameter : Error< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5db541f3a87eb..e54d9a50580dd 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3173,6 +3173,9 @@ def note_dependent_splice_explicit_this_may_fix : Note< def err_cannot_expand_over_type : Error< "cannot expand over an expression of type %0">; +def p3385_sema_trace_execution_checkpoint : Warning< + "(p3385-Sema) trace_execution_checkpoint %0">; + // C++11 char16_t/char32_t def warn_cxx98_compat_unicode_type : Warning< "'%0' type specifier is incompatible with C++98">, diff --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def index 4f826ac0b1cc0..f0941542cbfbe 100644 --- a/clang/include/clang/Basic/Features.def +++ b/clang/include/clang/Basic/Features.def @@ -310,6 +310,7 @@ FEATURE(cxx_abi_relative_vtable, LangOpts.CPlusPlus && LangOpts.RelativeCXXABIVT FEATURE(reflection, LangOpts.Reflection) FEATURE(reflection_new_syntax, LangOpts.ReflectionNewSyntax) FEATURE(parameter_reflection, LangOpts.ParameterReflection) +FEATURE(attribute_reflection, LangOpts.AttributeReflection) FEATURE(expansion_statements, LangOpts.ExpansionStatements) FEATURE(annotation_attributes, LangOpts.AnnotationAttributes) FEATURE(consteval_blocks, LangOpts.ConstevalBlocks) diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index b30ecdf17175c..e0ed057032c07 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -314,6 +314,7 @@ LANGOPT(OpenACC , 1, 0, "OpenACC Enabled") LANGOPT(Reflection , 1, 0, "Experimental C++26 Reflection") LANGOPT(ReflectionNewSyntax , 1, 0, "New syntax for C++26 Reflection") LANGOPT(ParameterReflection , 1, 0, "Augments C++26 Reflection with function parameter reflection") +LANGOPT(AttributeReflection , 1, 0, "Augments C++26 Reflection with standard attribute reflection") LANGOPT(ExpansionStatements , 1, 0, "Experimental C++26 Expansion Statements") LANGOPT(AnnotationAttributes, 1, 0, "Experimental C++26 support for annotations") LANGOPT(ConstevalBlocks, 1, 0, "Experimental C++26 support for consteval blocks") diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 255d2ef84a55a..67a6f1056d30b 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3504,6 +3504,11 @@ defm parameter_reflection : BoolFOption<"parameter-reflection", PosFlag, NegFlag>; +defm attribute_reflection : BoolFOption<"attribute-reflection", + LangOpts<"AttributeReflection">, DefaultFalse, + PosFlag, + NegFlag>; defm expansion_statements : BoolFOption<"expansion-statements", LangOpts<"ExpansionStatements">, DefaultFalse, PosFlaggetAttrName()->getName()); + if(attr->getNumArgs() > 0) + { + auto *Arg0 = attr->getArgAsExpr(0); + StringLiteral *Literal = + dyn_cast(Arg0->IgnoreParenCasts()); + if(Literal) { + StringRef String = Literal->getString(); + ID.AddInteger(String.size()); + ID.AddString(String.data()); + } + } + + return; + } case ReflectionKind::DataMemberSpec: { TagDataMemberSpec *TDMS = V.getReflectedDataMemberSpec(); TDMS->Ty.Profile(ID); @@ -942,6 +960,13 @@ CXX26AnnotationAttr *APValue::getReflectedAnnotation() const { const_cast(getOpaqueReflectionData())); } +ParsedAttr *APValue::getReflectedAttribute() const { + assert(getReflectionKind() == ReflectionKind::Attribute && + "not a reflection of an attribute"); + return reinterpret_cast( + const_cast(getOpaqueReflectionData())); +} + static double GetApproxValue(const llvm::APFloat &F) { llvm::APFloat V = F; bool ignored; @@ -1295,6 +1320,9 @@ void APValue::printPretty(raw_ostream &Out, const PrintingPolicy &Policy, case ReflectionKind::Annotation: Repr = "annotation"; break; + case ReflectionKind::Attribute: + Repr = "attribute"; + break; } Out << "^^(" << Repr << ")"; return; @@ -1635,6 +1663,7 @@ void APValue::setReflection(ReflectionKind RK, const void *Ptr) { case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: SelfData.Kind = RK; SelfData.Data = Ptr; return; diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index a2608a5d0dd2a..73327c39b6e74 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -986,6 +986,7 @@ ExprDependence clang::computeDependence(CXXReflectExpr *E, case ReflectionKind::Namespace: case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: + case ReflectionKind::Attribute: return ExprDependence::None; } llvm_unreachable("unknown reflection kind while computing dependence"); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 98c4e7bd2eeda..21efd9ab17ea2 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -18,6 +18,7 @@ #include "clang/AST/ASTMutationListener.h" #include "clang/AST/Attr.h" #include "clang/AST/AttrIterator.h" +#include "clang/AST/Attrs.inc" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclContextInternals.h" @@ -1013,6 +1014,13 @@ void Decl::dropAttrs() { getASTContext().eraseDeclAttrs(this); } +void Decl::addAttr(Attr *A, const ParsedAttr* pA) { + assert((pA != nullptr || DelayedSpliceAttr::classof(A)) && "Found null syntactic attribute while creating semantic attribute"); + A->setParsedAttr(pA); + this->addAttr(A); +} + + void Decl::addAttr(Attr *A) { if (!hasAttrs()) { setAttrs(AttrVec(1, A)); diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index aa1a14ac9ca74..98048ecbd1172 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -14,6 +14,7 @@ #include "clang/AST/APValue.h" #include "clang/AST/Attr.h" +#include "clang/AST/Attrs.inc" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" @@ -22,15 +23,15 @@ #include "clang/AST/Metafunction.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Reflection.h" +#include "clang/AST/Type.h" +#include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/DiagnosticMetafn.h" -#include "clang/Basic/SourceManager.h" +#include "clang/Basic/IdentifierTable.h" #include "clang/Lex/Lexer.h" -#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/ParsedAttr.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" - namespace clang { using EvalFn = Metafunction::EvaluateFn; @@ -558,6 +559,19 @@ static bool annotate(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, SourceRange Range, ArrayRef Args); +// ----------------------------------------------------------------------------- +// P3385 Metafunction declarations +// ----------------------------------------------------------------------------- + +static bool get_ith_attribute_of(APValue &Result, ASTContext &C, + MetaActions &Meta, EvalFn Evaluator, + DiagFn Diagnoser, QualType ResultTy, + SourceRange Range, + ArrayRef Args); + +static bool is_attribute(APValue &Result, ASTContext &C, MetaActions &Meta, + EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, + SourceRange Range, ArrayRef Args); // ----------------------------------------------------------------------------- // Metafunction table @@ -686,6 +700,11 @@ static constexpr Metafunction Metafunctions[] = { { Metafunction::MFRK_metaInfo, 3, 3, get_ith_annotation_of }, { Metafunction::MFRK_bool, 1, 1, is_annotation }, { Metafunction::MFRK_metaInfo, 2, 2, annotate }, + + // P3385 attributes reflection + { Metafunction::MFRK_metaInfo, 3, 3, get_ith_attribute_of }, + { Metafunction::MFRK_bool, 1, 1, is_attribute }, + }; constexpr const unsigned NumMetafunctions = sizeof(Metafunctions) / sizeof(Metafunction); @@ -748,6 +767,10 @@ static APValue makeReflection(CXX26AnnotationAttr *A) { return APValue(ReflectionKind::Annotation, A); } +static APValue makeReflection(const ParsedAttr * Attr) { + return APValue(ReflectionKind::Attribute, Attr); +} + static Expr *makeStrLiteral(StringRef Str, ASTContext &C, bool Utf8) { QualType ConstCharTy = (Utf8 ? C.Char8Ty : C.CharTy).withConst(); @@ -938,6 +961,15 @@ static bool findAnnotLoc(APValue &Result, ASTContext &C, EvalFn Evaluator, return !Evaluator(Result, SLE, true); } +static bool findAttrLoc(APValue &Result, ASTContext &C, EvalFn Evaluator, + QualType ResultTy, AttributeCommonInfo *A) { + SourceLocExpr *SLE = + new (C) SourceLocExpr(C, SourceLocIdentKind::SourceLocStruct, + ResultTy, A->getLoc(), SourceLocation(), + nullptr); + return !Evaluator(Result, SLE, true); +} + static QualType desugarType(QualType QT, bool UnwrapAliases, bool DropCV, bool DropRefs) { bool IsConst = QT.isConstQualified(); @@ -1396,6 +1428,9 @@ StringRef DescriptionOf(APValue RV, bool Granular = true) { case ReflectionKind::Annotation: { return "an annotation"; } + case ReflectionKind::Attribute: { + return "an attribute"; + } } } @@ -1412,10 +1447,165 @@ bool DiagnoseReflectionKind(DiagFn Diagnoser, SourceRange Range, return true; } +struct AttributeScratchpad { + AttributeFactory factory; + ParsedAttributes attributes; + ArgsVector ArgExprs; + bool argFound; + AttributeScratchpad() : factory(), attributes(factory), ArgExprs(), argFound(false) {} +}; + // ----------------------------------------------------------------------------- // Metafunction implementations // ----------------------------------------------------------------------------- +bool get_ith_attribute_of(APValue &Result, ASTContext &C, + MetaActions &Meta, EvalFn Evaluator, + DiagFn Diagnoser, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == C.MetaInfoTy); + + APValue RV; + if (!Evaluator(RV, Args[0], true)) + return true; + + APValue Sentinel; + if (!Evaluator(Sentinel, Args[1], true)) + return true; + assert(Sentinel.isReflectedType()); + + APValue Idx; + if (!Evaluator(Idx, Args[2], true)) + return true; + size_t idx = Idx.getInt().getExtValue(); + + switch (RV.getReflectionKind()) { + case ReflectionKind::Attribute: { + if (idx != 0) { + return SetAndSucceed(Result, Sentinel); + } + ParsedAttr *attr = RV.getReflectedAttribute(); + if (attr->getForm().getSyntax() == AttributeCommonInfo::Syntax::AS_CXX11) { + return SetAndSucceed(Result, makeReflection(attr)); + } + // Non standard + return Diagnoser(Range.getBegin(), diag::metafn_p3385_non_standard_attribute) << attr->getAttrName(); + } + case ReflectionKind::Type: { + QualType qType = RV.getReflectedType(); + Decl *D = findTypeDecl(qType); + if (!D) { + // FIXME how would we end up here ? + return DiagnoseReflectionKind(Diagnoser, Range, "attribute or type", + DescriptionOf(RV)); + } + auto attrs = D->attrs(); + + if (attrs.empty()) { + return SetAndSucceed(Result, Sentinel); + } + + std::vector cxx11Attrs; + // poor man ::filter, copy_if, etc.... + for (Attr *const *attr = attrs.begin(); attr != attrs.end(); ++attr) { + if ((*attr)->isCXX11Attribute()){ + cxx11Attrs.push_back(attr); + } + } + + if (idx >= cxx11Attrs.size()) { + return SetAndSucceed(Result, Sentinel); + } + + // Attr -> ParsedAttr + Attr * const val = *cxx11Attrs[idx]; + assert(val); + static AttributeScratchpad scratchpad; + + const ParsedAttr * parsedAttr = val->fromParsedAttr(); + assert(parsedAttr && "no backlink from semantic attribute"); + + if (scratchpad.argFound = parsedAttr->getNumArgs() != 0; scratchpad.argFound) { + scratchpad.ArgExprs.push_back(parsedAttr->getArg(0)); + } else { + scratchpad.ArgExprs.clear(); + } + auto * fetchedAttribute = scratchpad.attributes.addNew( + const_cast(parsedAttr->getAttrName()), + val->getRange(), + nullptr, + val->getLoc(), + scratchpad.ArgExprs.data(), scratchpad.argFound, + parsedAttr->getForm() + ); + return SetAndSucceed(Result, makeReflection(fetchedAttribute)); + } + case ReflectionKind::Declaration: { + ValueDecl *D = RV.getReflectedDecl(); + + if (!D) { + // FIXME how would we end up here ? + return DiagnoseReflectionKind(Diagnoser, Range, "attribute or type", + DescriptionOf(RV)); + } + + auto attrs = D->attrs(); + if (attrs.empty()) { + return SetAndSucceed(Result, Sentinel); + } + + std::vector cxx11Attrs; + // poor man ::filter, copy_if, etc.... + for (Attr *const *attr = attrs.begin(); attr != attrs.end(); ++attr) { + if ((*attr)->isCXX11Attribute() && !DelayedSpliceAttr::classof(*attr)){ + cxx11Attrs.push_back(attr); + } + } + + if (idx >= cxx11Attrs.size()) { + return SetAndSucceed(Result, Sentinel); + } + + // Attr -> ParsedAttr + Attr * const val = *cxx11Attrs[idx]; + assert(val); + static AttributeScratchpad scratchpad; + + const ParsedAttr * parsedAttr = val->fromParsedAttr(); + assert(parsedAttr && "no backlink from semantic attribute"); + + scratchpad.ArgExprs.clear(); + if (scratchpad.argFound = parsedAttr->getNumArgs() != 0; scratchpad.argFound) { + scratchpad.ArgExprs.push_back(parsedAttr->getArg(0)); + } + + auto * fetchedAttribute = scratchpad.attributes.addNew( + const_cast(parsedAttr->getAttrName()), + val->getRange(), + nullptr, + val->getLoc(), + scratchpad.ArgExprs.data(), scratchpad.argFound, + parsedAttr->getForm() + ); + return SetAndSucceed(Result, makeReflection(fetchedAttribute)); + + } + case ReflectionKind::Null: + case ReflectionKind::Template: + case ReflectionKind::Object: + case ReflectionKind::Value: + case ReflectionKind::Namespace: + case ReflectionKind::BaseSpecifier: + case ReflectionKind::DataMemberSpec: + case ReflectionKind::Annotation: + return DiagnoseReflectionKind(Diagnoser, Range, "declaration or attribute", + DescriptionOf(RV)); + } + llvm_unreachable("unknown reflection kind"); + return false; +} + bool get_begin_enumerator_decl_of(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, @@ -1453,6 +1643,7 @@ bool get_begin_enumerator_decl_of(APValue &Result, ASTContext &C, case ReflectionKind::Namespace: case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: + case ReflectionKind::Attribute: case ReflectionKind::Annotation: { return DiagnoseReflectionKind(Diagnoser, Range, "an enum type", DescriptionOf(RV)); @@ -1493,6 +1684,7 @@ bool get_next_enumerator_decl_of(APValue &Result, ASTContext &C, case ReflectionKind::Namespace: case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: + case ReflectionKind::Attribute: case ReflectionKind::Annotation: { llvm_unreachable("should have failed in 'get_begin_enumerator_decl_of'"); } @@ -1550,6 +1742,7 @@ bool get_ith_base_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return DiagnoseReflectionKind(Diagnoser, Range, "a class type", DescriptionOf(RV)); } @@ -1604,6 +1797,7 @@ bool get_ith_template_argument_of(APValue &Result, ASTContext &C, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return DiagnoseReflectionKind(Diagnoser, Range, "a template specialization", DescriptionOf(RV)); } @@ -1693,6 +1887,7 @@ bool get_begin_member_decl_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return true; } llvm_unreachable("unknown reflection kind"); @@ -1871,6 +2066,11 @@ bool identifier_of(APValue &Result, ASTContext &C, MetaActions &Meta, getDeclName(Name, C, RV.getReflectedNamespace()); break; } + case ReflectionKind::Attribute: { + AttributeCommonInfo* attr = RV.getReflectedAttribute(); + Name = attr->getAttrName()->getName(); + break; + } case ReflectionKind::DataMemberSpec: { TagDataMemberSpec *TDMS = RV.getReflectedDataMemberSpec(); if (TDMS->Name) @@ -1957,6 +2157,11 @@ bool has_identifier(APValue &Result, ASTContext &C, MetaActions &Meta, HasIdentifier = TDMS->Name && !TDMS->Name->empty(); break; } + case ReflectionKind::Attribute: { + // FIXME deal with ^^ [[ ]] + HasIdentifier = true; + break; + } case ReflectionKind::Null: case ReflectionKind::BaseSpecifier: case ReflectionKind::Object: @@ -2049,6 +2254,9 @@ bool source_location_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Annotation: return findAnnotLoc(Result, C, Evaluator, ResultTy, RV.getReflectedAnnotation()); + case ReflectionKind::Attribute: + return findAttrLoc(Result, C, Evaluator, ResultTy, + RV.getReflectedAttribute()); case ReflectionKind::Object: case ReflectionKind::Value: case ReflectionKind::Null: @@ -2070,6 +2278,7 @@ bool type_of(APValue &Result, ASTContext &C, MetaActions &Meta, switch (RV.getReflectionKind()) { case ReflectionKind::Null: case ReflectionKind::Type: + case ReflectionKind::Attribute: case ReflectionKind::Template: case ReflectionKind::Namespace: return Diagnoser(Range.getBegin(), diag::metafn_no_associated_property) @@ -2145,6 +2354,7 @@ bool parent_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: if (Diagnoser) return Diagnoser(Range.getBegin(), diag::metafn_no_associated_property) << DescriptionOf(RV) << 1 << Range; @@ -2198,6 +2408,7 @@ bool dealias(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, RV); case ReflectionKind::Type: { QualType QT = RV.getReflectedType(); @@ -2259,6 +2470,7 @@ bool object_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) << 1 << DescriptionOf(RV) << Range; } @@ -2348,6 +2560,7 @@ bool value_of(APValue &Result, ASTContext &C, MetaActions &Meta, /*DropCV=*/true, /*DropRefs=*/false); return SetAndSucceed(Result, A->getValue().Lift(Ty)); } + case ReflectionKind::Attribute: // TODO P3385 anything to do ? case ReflectionKind::Null: case ReflectionKind::Type: case ReflectionKind::Template: @@ -2395,6 +2608,7 @@ bool template_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return DiagnoseReflectionKind(Diagnoser, Range, "a template specialization", DescriptionOf(RV)); return true; @@ -2417,6 +2631,7 @@ static bool CanActAsTemplateArg(const APValue &RV) { case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: case ReflectionKind::Null: return false; } @@ -2837,6 +3052,7 @@ bool extract(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Type: case ReflectionKind::Template: case ReflectionKind::Namespace: + case ReflectionKind::Attribute: case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: return Diagnoser(Range.getBegin(), diag::metafn_cannot_extract) @@ -2884,6 +3100,7 @@ bool is_public(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: case ReflectionKind::Namespace: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); } llvm_unreachable("invalid reflection type"); @@ -2927,6 +3144,7 @@ bool is_protected(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Value: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: case ReflectionKind::Namespace: return SetAndSucceed(Result, makeBool(C, false)); } @@ -2972,6 +3190,7 @@ bool is_private(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); } llvm_unreachable("invalid reflection type"); @@ -3026,6 +3245,7 @@ bool is_access_specified(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); } llvm_unreachable("invalid reflection type"); @@ -3134,6 +3354,7 @@ bool is_accessible(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return DiagnoseReflectionKind(Diagnoser, Range, "a class member", DescriptionOf(RV)); } @@ -3169,7 +3390,8 @@ bool is_virtual(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: - return SetAndSucceed(Result, makeBool(C, IsVirtual)); + case ReflectionKind::Attribute: + return SetAndSucceed(Result, makeBool(C, false)); } llvm_unreachable("invalid reflection type"); } @@ -3194,6 +3416,7 @@ bool is_pure_virtual(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { bool IsPureVirtual = false; @@ -3227,6 +3450,7 @@ bool is_override(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { if (const auto *MD = dyn_cast(RV.getReflectedDecl())) @@ -3257,6 +3481,7 @@ bool is_deleted(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { bool IsDeleted = false; @@ -3288,6 +3513,7 @@ bool is_defaulted(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { bool IsDefaulted = false; @@ -3319,6 +3545,7 @@ bool is_explicit(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: case ReflectionKind::Template: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { @@ -3354,6 +3581,7 @@ bool is_noexcept(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Type: { const QualType QT = RV.getReflectedType(); @@ -3425,6 +3653,7 @@ bool is_const(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Type: { bool result = isConstQualifiedType(RV.getReflectedType()); @@ -3465,6 +3694,7 @@ bool is_volatile(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Type: { bool result = isVolatileQualifiedType(RV.getReflectedType()); @@ -3807,7 +4037,8 @@ bool is_static_member(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: - return SetAndSucceed(Result, makeBool(C, result)); + case ReflectionKind::Attribute: + return SetAndSucceed(Result, makeBool(C, false)); } llvm_unreachable("unknown reflection kind"); } @@ -3851,6 +4082,18 @@ bool is_namespace(APValue &Result, ASTContext &C, MetaActions &Meta, return SetAndSucceed(Result, makeBool(C, RV.isReflectedNamespace())); } +bool is_attribute(APValue &Result, ASTContext &C, MetaActions &Meta, + EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, + SourceRange Range, ArrayRef Args) { + assert(Args[0]->getType()->isReflectionType()); + assert(ResultTy == C.BoolTy); + APValue RV; + if (!Evaluator(RV, Args[0], true)) + return true; + + return SetAndSucceed(Result, makeBool(C, RV.isReflectedAttribute())); +} + bool is_function(APValue &Result, ASTContext &C, MetaActions &Meta, EvalFn Evaluator, DiagFn Diagnoser, QualType ResultTy, SourceRange Range, ArrayRef Args) { @@ -3927,6 +4170,7 @@ bool is_alias(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); } llvm_unreachable("unknown reflection kind"); @@ -3988,6 +4232,7 @@ bool has_complete_definition(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: break; } @@ -4262,6 +4507,7 @@ bool has_template_arguments(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); } llvm_unreachable("unknown reflection kind"); @@ -4361,6 +4607,7 @@ bool is_constructor(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { bool result = isa(RV.getReflectedDecl()); @@ -4499,6 +4746,7 @@ bool is_destructor(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { bool result = isa(RV.getReflectedDecl()); @@ -4528,6 +4776,7 @@ bool is_special_member_function(APValue &Result, ASTContext &C, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return SetAndSucceed(Result, makeBool(C, false)); case ReflectionKind::Declaration: { bool IsSpecial = false; @@ -4783,6 +5032,7 @@ bool reflect_invoke(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_invoke) << DescriptionOf(FnRefl) << Range; case ReflectionKind::Object: { @@ -5146,6 +5396,7 @@ bool offset_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return DiagnoseReflectionKind(Diagnoser, Range, "a non-static data member", DescriptionOf(RV)); case ReflectionKind::Declaration: { @@ -5202,6 +5453,7 @@ bool size_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::BaseSpecifier: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) << 3 << DescriptionOf(RV); } @@ -5228,6 +5480,7 @@ bool bit_offset_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return DiagnoseReflectionKind(Diagnoser, Range, "a non-static data member", DescriptionOf(RV)); case ReflectionKind::Declaration: { @@ -5290,6 +5543,7 @@ bool bit_size_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::BaseSpecifier: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) << 3 << DescriptionOf(RV); } @@ -5353,6 +5607,7 @@ bool alignment_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::BaseSpecifier: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) << 4 << DescriptionOf(RV) << Range; } @@ -5541,6 +5796,7 @@ bool get_ith_parameter_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return true; } return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) @@ -5576,6 +5832,7 @@ bool has_consistent_identifier(APValue &Result, ASTContext &C, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return has_identifier(Result, C, Meta, Evaluator, Diagnoser, ResultTy, Range, Args); } @@ -5602,6 +5859,7 @@ bool has_ellipsis_parameter(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) << 5 << DescriptionOf(RV) << Range; case ReflectionKind::Type: @@ -5648,6 +5906,7 @@ bool has_default_argument(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return DiagnoseReflectionKind(Diagnoser, Range, "a function parameter", DescriptionOf(RV)); } @@ -5722,6 +5981,7 @@ bool return_type_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) << 6 << DescriptionOf(RV) << Range; } @@ -5794,6 +6054,7 @@ bool get_ith_annotation_of(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_query_property) << 7 << DescriptionOf(RV) << Range; } @@ -5868,6 +6129,7 @@ bool annotate(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: return Diagnoser(Range.getBegin(), diag::metafn_cannot_annotate) << DescriptionOf(Appertainee) << Range; } diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 0c85156489e49..7e287626a7cc7 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7416,6 +7416,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // -fparameter-reflection is likewise off by default. Args.addOptInFlag(CmdArgs, options::OPT_fparameter_reflection, options::OPT_fno_parameter_reflection); + // -fattribute-reflection is likewise off by default. + Args.addOptInFlag(CmdArgs, options::OPT_fattribute_reflection, + options::OPT_fno_attribute_reflection); // -fexpansion-statements is likewise off by default. Args.addOptInFlag(CmdArgs, options::OPT_fexpansion_statements, options::OPT_fno_expansion_statements); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 7e9119f27857b..c40a7d4cb122f 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -615,8 +615,12 @@ static bool FixupInvocation(CompilerInvocation &Invocation, if (LangOpts.Reflection) { if (LangOpts.Blocks && !LangOpts.ReflectionNewSyntax) Diags.Report(diag::err_fe_reflection_incompatible_with_blocks); - } else if (LangOpts.ParameterReflection) { - Diags.Report(diag::err_fe_parameter_reflection_without_reflection); + } else { + if (LangOpts.ParameterReflection) { + Diags.Report(diag::err_fe_parameter_reflection_without_reflection); + } else if (LangOpts.AttributeReflection) { + Diags.Report(diag::err_fe_attribute_reflection_without_reflection); + } } // The -f[no-]raw-string-literals option is only valid in C and in C++ diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index fe46d6a86da5a..198eed0876750 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -14,10 +14,14 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyDeclStackTrace.h" +#include "clang/AST/Reflection.h" #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Basic/Attributes.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/DiagnosticParse.h" +#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/OperatorKinds.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TokenKinds.h" @@ -32,6 +36,7 @@ #include "clang/Sema/SemaCodeCompletion.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/TimeProfiler.h" +#include #include using namespace clang; @@ -5041,11 +5046,169 @@ bool Parser::ParseCXX11AttributeArgs( return true; } +// FIXME I'm copypasting this straight from ExprConstantMeta +// +static NamedDecl *findTypeDecl(QualType QT) { + // If it's an ElaboratedType, get the underlying NamedType. + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + + // Get the type's declaration. + NamedDecl *D = nullptr; + if (auto *TDT = dyn_cast(QT)) + D = TDT->getDecl(); + else if (auto *UT = dyn_cast(QT)) + D = UT->getFoundDecl(); + else if (auto *TD = QT->getAsTagDecl()) + return TD; + else if (auto *TT = dyn_cast(QT)) + D = TT->getDecl(); + else if (auto *UUTD = dyn_cast(QT)) + D = UUTD->getDecl(); + else if (auto *TS = dyn_cast(QT)) { + if (auto *CTD = dyn_cast( + TS->getTemplateName().getAsTemplateDecl())) { + void *InsertPos; + D = CTD->findSpecialization(TS->template_arguments(), InsertPos); + } + } else if (auto *STTP = dyn_cast(QT)) + D = findTypeDecl(STTP->getReplacementType()); + else if (auto *ICNT = dyn_cast(QT)) + D = ICNT->getDecl(); + else if (auto *DTT = dyn_cast(QT)) + D = findTypeDecl(DTT->getUnderlyingType()); + + return D; +} + +// FIXME this is +/- get_ith_attributes, repeated here +// +bool Parser::tryParseSpliceAttrSpecifier(ParsedAttributes &Attrs, + SourceLocation *EndLoc) +{ + // Try parsing a [: meta-info :] immediately following '[' '[' + if (!Tok.is(tok::l_splice)) { + return true; + } + if(!getLangOpts().AttributeReflection) { + Diag(Tok.getLocation(), diag::p3385_err_attribute_splicing_error) + << "attribute splicing is gated behind -fattribute-reflection"; + return true; + } + + if (ParseCXXSpliceSpecifier()) { + // FIXME diagnostic is terrible... + Diag(Tok.getLocation(), diag::p3385_err_attribute_splicing_error) + << "invalid expression in attribute splicing"; + return true; + } + if (!Tok.is(tok::annot_splice)) { + return true; + } + Diag(Tok.getLocation(), diag::p3385_trace_execution_checkpoint) + << "Found splice in attribute expression"; + SourceLocation loc = (Tok.getLocation()); + SourceRange range(loc); + + // FIXME What's this doing ? + ExprResult Result = getExprAnnotation(Tok); + ConsumeAnnotationToken(); + + ArgsVector ArgExprs; + auto *SpliceExpr = cast(Result.get()); + + // In `template [[ [: ^^T :] ]] ... ` ^^T is found to be a value dependent + // expression and EvaluateAsRValue die... so for now we ll smuggle the expression in + // [[clang::DelayedSplice(expr)]] and we'll evaluate it at instantiation + if (SpliceExpr->isValueDependent()) { + Diag(Tok.getLocation(), diag::p3385_trace_execution_checkpoint) + << "Found value dependent expression in attribute splicing, creating 'DelayedSpliceAttr'"; + + IdentifierInfo& delayedAttributeName = PP.getIdentifierTable().getOwn("clang::DelayedSplice"); + + ArgExprs.push_back(SpliceExpr); + Attrs.addNew( + &delayedAttributeName, + range, + nullptr, loc, ArgExprs.data(), ArgExprs.size(), + ParsedAttr::Form::CXX11()); + // Early return if we find a splice... there should be nothing else in the [[ ]] + return true; + } + Expr::EvalResult ER; + if (!SpliceExpr->EvaluateAsRValue(ER, Actions.getASTContext(), true)) { + return false; + } + + assert(ER.Val.getKind() == APValue::Reflection); + switch (ER.Val.getReflectionKind()) { + case ReflectionKind::Attribute: { + auto * attr = ER.Val.getReflectedAttribute(); + if (attr->getNumArgs() != 0) { + Diag(Tok.getLocation(), diag::p3385_trace_execution_checkpoint) + << "Found argument while splicing a reflected attribute"; + ArgExprs.push_back(attr->getArg(0)); + } + Attrs.addNew( + const_cast(attr->getAttrName()), + range, + nullptr, loc, ArgExprs.data(), ArgExprs.size(), + ParsedAttr::Form::CXX11()); + break; + } + case ReflectionKind::Type: { + QualType qType = ER.Val.getReflectedType(); + NamedDecl *D = findTypeDecl(qType); + if (!D) { + Diag(Tok.getLocation(), diag::p3385_err_attribute_splicing_error) + << "Error no declaration found related to the type"; + return true; + } + for (auto *const attr : D->attrs()) { + ArgExprs.clear(); + // Only splice [[ ]] attributes + if (!attr->isCXX11Attribute()) { + continue; + } + // We dont copy over spliced attributes + if (DelayedSpliceAttr::classof(attr)) { + continue; + } + const ParsedAttr * parsedAttr = attr->fromParsedAttr(); + + if (!parsedAttr) { + Diag(Tok.getLocation(), diag::p3385_err_attribute_splicing_error) + << "Found no backlink, ignoring attribute arguments"; + } else if (size_t nbArgs = parsedAttr->getNumArgs(); nbArgs >0) { + Diag(Tok.getLocation(), diag::p3385_trace_execution_checkpoint) + << "Found argument(s) while splicing a reflected attribute"; + for (size_t i = 0; i != nbArgs; ++i) { + ArgExprs.push_back(parsedAttr->getArg(i)); + } + } + Attrs.addNew( + const_cast(attr->getAttrName()), + range, + nullptr, loc, ArgExprs.data(), ArgExprs.size(), + ParsedAttr::Form::CXX11() + ); + } + break; + } + default: + Diag(Tok.getLocation(), diag::p3385_err_attribute_splicing_error) + << "Only reflection of 'attribute' or 'type' is supported in " + "attribute splicing"; + } + return false; +} + /// Parse a C++11 or C23 attribute-specifier. /// /// [C++11] attribute-specifier: /// '[' '[' attribute-list ']' ']' /// alignment-specifier +/// '[' '[' splice-name-qualifier ']' ']' /// /// [C++11] attribute-list: /// attribute[opt] @@ -5115,7 +5278,8 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs, SourceLocation CommonScopeLoc; IdentifierInfo *CommonScopeName = nullptr; - if (Tok.is(tok::kw_using)) { + bool hasAttributeUsing = Tok.is(tok::kw_using); + if (hasAttributeUsing) { Diag(Tok.getLocation(), getLangOpts().CPlusPlus17 ? diag::warn_cxx14_compat_using_attribute_ns : diag::ext_using_attribute_ns); @@ -5132,6 +5296,15 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs, Diag(Tok.getLocation(), diag::err_expected) << tok::colon; } + // Try parsing a `[: :]` expression, will update `Attrs` as needed + if (!tryParseSpliceAttrSpecifier(Attrs, EndLoc)) { + if (hasAttributeUsing) { + Diag(Tok.getLocation(), diag::p3385_err_attribute_splicing_with_using_namespace_error) + << "Using prefix is not supported alongside a splice expression in attributes"; + return; + } + } + bool AttrParsed = false; while (!Tok.isOneOf(tok::r_square, tok::semi, tok::eof, tok::r_splice)) { if (AttrParsed) { @@ -5151,6 +5324,8 @@ void Parser::ParseCXX11AttributeSpecifierInternal(ParsedAttributes &Attrs, SourceLocation ScopeLoc, AttrLoc; IdentifierInfo *ScopeName = nullptr, *AttrName = nullptr; + + if (Tok.is(tok::equal) && getLangOpts().AnnotationAttributes) { // This is a C++2c annotation. if (CommonScopeName) { diff --git a/clang/lib/Parse/ParseReflect.cpp b/clang/lib/Parse/ParseReflect.cpp index f76bb6b1c7ee7..9b0de2401b5cd 100644 --- a/clang/lib/Parse/ParseReflect.cpp +++ b/clang/lib/Parse/ParseReflect.cpp @@ -12,10 +12,12 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/Attr.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" +#include "clang/Sema/Ownership.h" using namespace clang; ExprResult Parser::ParseCXXReflectExpression(SourceLocation OpLoc) { @@ -79,6 +81,27 @@ ExprResult Parser::ParseCXXReflectExpression(SourceLocation OpLoc) { } TentativeAction.Revert(); + // Check for a standard attribute + { + size_t last = Attrs.size(); + if (MaybeParseCXX11Attributes(Attrs)) { + size_t newLast = Attrs.size(); + Diag(OperandLoc, diag::p3385_trace_attribute_parsed); + + // FIXME handle empty [[]] gracefully + if (last == newLast) { + Diag(OperandLoc, diag::p3385_trace_empty_attributes_list); + return ExprError(); + } + if (newLast - last > 1) { + Diag(OperandLoc, diag::p3385_err_attributes_list) << (newLast - last); + return ExprError(); + } + + return Actions.ActOnCXXReflectExpr(OpLoc, &Attrs.back()); + } + } + if (SS.isSet() && TryAnnotateTypeOrScopeTokenAfterScopeSpec(SS, true, ImplicitTypenameContext::No)) { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 7f7851cc45153..4e1bd266bd24f 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -21,6 +21,7 @@ #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/ParsedAttr.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/SemaCodeCompletion.h" @@ -57,7 +58,7 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) : PP(pp), PreferredType(pp.isCodeCompletionEnabled()), Actions(actions), Diags(PP.getDiagnostics()), GreaterThanIsOperator(true), ColonIsSacred(false), InMessageExpression(false), - TemplateParameterDepth(0), ParsingInObjCContainer(false) { + TemplateParameterDepth(0), Attrs(AttrFactory), ParsingInObjCContainer(false) { SkipFunctionBodies = pp.isCodeCompletionEnabled() || skipFunctionBodies; Tok.startToken(); Tok.setKind(tok::eof); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 048c374e34fd6..f0a3489771f19 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -82,6 +82,18 @@ namespace AttributeLangSupport { }; } // end namespace AttributeLangSupport +namespace { + +struct AttributeScratchpad { + AttributeFactory factory; + ParsedAttributes attributes; + ArgsVector ArgExprs; + bool argFound; + AttributeScratchpad() : factory(), attributes(factory), ArgExprs(), argFound(false) {} +}; + +} // namespace + static unsigned getNumAttributeArgs(const ParsedAttr &AL) { // FIXME: Include the type in the argument list. return AL.getNumArgs() + AL.hasParsedType(); @@ -2013,7 +2025,20 @@ static void handleUnusedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!S.getLangOpts().CPlusPlus17 && IsCXX17Attr) S.Diag(AL.getLoc(), diag::ext_cxx17_attr) << AL; - D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL)); + static AttributeScratchpad scratchpad; + if (scratchpad.argFound = AL.getNumArgs() != 0; scratchpad.argFound) { + scratchpad.ArgExprs.push_back(AL.getArg(0)); + } else { + scratchpad.ArgExprs.clear(); + } + auto * stashedSyntacticAttribute = scratchpad.attributes.addNew( + const_cast(AL.getAttrName()), + AL.getRange(), + nullptr, + AL.getLoc(), + scratchpad.ArgExprs.data(), scratchpad.argFound, + AL.getForm()); + D->addAttr(::new (S.Context) UnusedAttr(S.Context, AL), stashedSyntacticAttribute); } static void handleConstructorAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -2745,6 +2770,12 @@ static void handleSentinelAttr(Sema &S, Decl *D, const ParsedAttr &AL) { D->addAttr(::new (S.Context) SentinelAttr(S.Context, AL, sentinel, nullPos)); } +static void handlDelayedSpliceAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + CXXSpliceSpecifierExpr * expr = static_cast(AL.getArgAsExpr(0)); + // Note the backlink is meaningless here... + D->addAttr(::new (S.Context) DelayedSpliceAttr(S.Context, AL, expr), nullptr); +} + static void handleWarnUnusedResult(Sema &S, Decl *D, const ParsedAttr &AL) { if (D->getFunctionType() && D->getFunctionType()->getReturnType()->isVoidType() && @@ -2795,7 +2826,23 @@ static void handleWarnUnusedResult(Sema &S, Decl *D, const ParsedAttr &AL) { return; } - D->addAttr(::new (S.Context) WarnUnusedResultAttr(S.Context, AL, Str)); + static AttributeScratchpad scratchpad; + if (scratchpad.argFound = AL.getNumArgs() != 0; scratchpad.argFound) { + scratchpad.ArgExprs.push_back(AL.getArg(0)); + } else { + scratchpad.ArgExprs.clear(); + } + auto * stashedSyntacticAttribute = scratchpad.attributes.addNew( + const_cast(AL.getAttrName()), + AL.getRange(), + nullptr, + AL.getLoc(), + scratchpad.ArgExprs.data(), scratchpad.argFound, + AL.getForm() + ); + + // Add semantic attribute and backlink to syntactic one + D->addAttr(::new (S.Context) WarnUnusedResultAttr(S.Context, AL, Str), stashedSyntacticAttribute); } static void handleWeakImportAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -5866,7 +5913,23 @@ static void handleDeprecatedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { if (!S.getLangOpts().CPlusPlus14 && AL.isCXX11Attribute() && !AL.isGNUScope()) S.Diag(AL.getLoc(), diag::ext_cxx14_attr) << AL; - D->addAttr(::new (S.Context) DeprecatedAttr(S.Context, AL, Str, Replacement)); + static AttributeScratchpad scratchpad; + if (scratchpad.argFound = AL.getNumArgs() != 0; scratchpad.argFound) { + scratchpad.ArgExprs.push_back(AL.getArg(0)); + } else { + scratchpad.ArgExprs.clear(); + } + auto * stashedSyntacticAttribute = scratchpad.attributes.addNew( + const_cast(AL.getAttrName()), + AL.getRange(), + nullptr, + AL.getLoc(), + scratchpad.ArgExprs.data(), scratchpad.argFound, + AL.getForm() + ); + + // Add semantic attribute and backlink to syntactic one + D->addAttr(::new (S.Context) DeprecatedAttr(S.Context, AL, Str, Replacement), stashedSyntacticAttribute); } static bool isGlobalVar(const Decl *D) { @@ -6845,6 +6908,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_TypeVisibility: handleVisibilityAttr(S, D, AL, true); break; + case ParsedAttr::AT_DelayedSplice: + handlDelayedSpliceAttr(S, D, AL); + break; case ParsedAttr::AT_WarnUnusedResult: handleWarnUnusedResult(S, D, AL); break; diff --git a/clang/lib/Sema/SemaReflect.cpp b/clang/lib/Sema/SemaReflect.cpp index 15ccf9cf6396f..18d0c85bc024b 100644 --- a/clang/lib/Sema/SemaReflect.cpp +++ b/clang/lib/Sema/SemaReflect.cpp @@ -21,6 +21,7 @@ #include "clang/Basic/DiagnosticSema.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Ownership.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Sema.h" #include "clang/Sema/Template.h" @@ -741,6 +742,11 @@ ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OperatorLoc, return BuildCXXReflectExpr(OperatorLoc, E); } +ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OperatorLoc, + ParsedAttr *A) { + return BuildCXXReflectExpr(OperatorLoc, A); +} + /// Returns an expression representing the result of a metafunction operating /// on a reflection. ExprResult Sema::ActOnCXXMetafunction(SourceLocation KwLoc, @@ -955,6 +961,10 @@ ParsedTemplateArgument Sema::ActOnTemplateSpliceSpecifierArgument( Diag(Splice->getExprLoc(), diag::err_unsupported_splice_kind) << "data member specs" << 0 << 0; break; + case ReflectionKind::Attribute: + Diag(Splice->getExprLoc(), diag::err_unsupported_splice_kind) + << "attribute" << 0 << 0; + break; } return ParsedTemplateArgument(); } @@ -1127,6 +1137,16 @@ ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc, ER.Val); } +ExprResult Sema::BuildCXXReflectExpr(SourceLocation OperatorLoc, + ParsedAttr *A) { + Diag(A->getLoc(), diag::p3385_sema_trace_execution_checkpoint) + << A->getAttrName()->getName(); + + return CXXReflectExpr::Create( + Context, OperatorLoc, A->getRange(), + APValue{ReflectionKind::Attribute, static_cast(A)}); +} + ExprResult Sema::BuildCXXMetafunctionExpr( SourceLocation KwLoc, SourceLocation LParenLoc, SourceLocation RParenLoc, unsigned MetaFnID, const CXXMetafunctionExpr::ImplFn &Impl, @@ -1481,6 +1501,7 @@ ExprResult Sema::BuildReflectionSpliceExpr( case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: Diag(SpliceOp->getOperand()->getExprLoc(), diag::err_unexpected_reflection_kind_in_splice) << 1 << SpliceOp->getOperand()->getSourceRange(); @@ -1609,6 +1630,7 @@ DeclContext *Sema::TryFindDeclContextOf(const Expr *E) { case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: Diag(E->getExprLoc(), diag::err_expected_class_or_namespace) << "spliced entity" << getLangOpts().CPlusPlus; return nullptr; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 5e3fb918103ec..9ab52a41bd57f 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -13,6 +13,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTMutationListener.h" +#include "clang/AST/Attrs.inc" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/DependentDiagnostic.h" @@ -21,6 +22,7 @@ #include "clang/AST/PrettyDeclStackTrace.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" +#include "clang/Basic/DiagnosticParse.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/EnterExpressionEvaluationContext.h" @@ -37,10 +39,46 @@ #include "clang/Sema/Template.h" #include "clang/Sema/TemplateInstCallback.h" #include "llvm/Support/TimeProfiler.h" +#include "llvm/Support/raw_ostream.h" #include using namespace clang; +// FIXME I'm copypasting this straight from ExprConstantMeta +// +static NamedDecl *findTypeDecl(QualType QT) { + // If it's an ElaboratedType, get the underlying NamedType. + if (const ElaboratedType *ET = dyn_cast(QT)) + QT = ET->getNamedType(); + + // Get the type's declaration. + NamedDecl *D = nullptr; + if (auto *TDT = dyn_cast(QT)) + D = TDT->getDecl(); + else if (auto *UT = dyn_cast(QT)) + D = UT->getFoundDecl(); + else if (auto *TD = QT->getAsTagDecl()) + return TD; + else if (auto *TT = dyn_cast(QT)) + D = TT->getDecl(); + else if (auto *UUTD = dyn_cast(QT)) + D = UUTD->getDecl(); + else if (auto *TS = dyn_cast(QT)) { + if (auto *CTD = dyn_cast( + TS->getTemplateName().getAsTemplateDecl())) { + void *InsertPos; + D = CTD->findSpecialization(TS->template_arguments(), InsertPos); + } + } else if (auto *STTP = dyn_cast(QT)) + D = findTypeDecl(STTP->getReplacementType()); + else if (auto *ICNT = dyn_cast(QT)) + D = ICNT->getDecl(); + else if (auto *DTT = dyn_cast(QT)) + D = findTypeDecl(DTT->getUnderlyingType()); + + return D; +} + static bool isDeclWithinFunction(const Decl *D) { const DeclContext *DC = D->getDeclContext(); if (DC->isFunctionOrMethod()) @@ -113,6 +151,59 @@ static void instantiateDependentAlignedAttr( } } +// We are not _really_ instantiating that attribute as it's merely +// a placeholder for the dependent type attributes we want to extract +// and appertain to 'New' +static void instantiateDelayedSpliceAttr( + Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, + const DelayedSpliceAttr *DelayedSplice, Decl *New) { + S.Diag(New->getLocation(), diag::p3385_trace_execution_checkpoint) << "In InstantiateAttrs"; + Expr* delayedSpliceExpr = DelayedSplice->getSpliceExpression(); + auto substResult = S.SubstExpr(delayedSpliceExpr, TemplateArgs); + if (Expr* splicedExpr = substResult.isInvalid() ? nullptr : substResult.get(); splicedExpr != nullptr) { + Expr::EvalResult ER; + if (!splicedExpr->EvaluateAsRValue(ER, S.getASTContext(), true)) { + S.Diag(New->getLocation(), diag::p3385_err_attribute_splicing_error) << "Error while EvaluateAsRValue"; + return; + } + SourceLocation loc = New->getLocation(); + SourceRange range(loc); + switch (ER.Val.getReflectionKind()) { + case ReflectionKind::Type: { + QualType qType = ER.Val.getReflectedType(); + NamedDecl *D = findTypeDecl(qType); + if (!D) { + S.Diag(loc, diag::p3385_err_attribute_splicing_error) + << "Error no declaration found related to the type"; + return; + } + for (auto *const attr : D->attrs()) { + // We dont attach another DelayedSplice attr + if (DelayedSpliceAttr::classof(attr)) { + S.Diag(loc, diag::p3385_trace_execution_checkpoint) << "Not attaching 'DelayedSpliceAttr'"; + continue; + } + // Only splice [[ ]] attributes + if (!attr->isCXX11Attribute()) { + S.Diag(loc, diag::p3385_trace_execution_checkpoint) << "(1/2) Skipping non CXX11 attribute..."; + S.Diag(loc, diag::p3385_trace_execution_checkpoint) << attr->getAttrName(); + continue; + } + S.Diag(loc, diag::p3385_trace_execution_checkpoint) << "(1/2) Attaching attribute..."; + S.Diag(loc, diag::p3385_trace_execution_checkpoint) << attr->getAttrName(); + + New->addAttr(attr); + New->dropAttr(); + } + break; + } + default: + S.Diag(loc, diag::p3385_err_attribute_splicing_error) + << "Only reflection of 'type' is supported in dependent attribute splicing"; + } + } +} + static void instantiateDependentAlignedAttr( Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs, const AlignedAttr *Aligned, Decl *New) { @@ -747,6 +838,13 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, LateInstantiatedAttrVec *LateAttrs, LocalInstantiationScope *OuterMostScope) { for (const auto *TmplAttr : Tmpl->attrs()) { + + if (const auto *DelayedSplice = dyn_cast(TmplAttr)) { + // Act on stashed spliced reflection attribute + instantiateDelayedSpliceAttr(*this, TemplateArgs, DelayedSplice, New); + continue; + } + if (!isRelevantAttr(*this, New, TmplAttr)) continue; diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 6c38df3143e7d..5be1edf049065 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -8968,6 +8968,7 @@ TreeTransform::TransformCXXReflectExpr(CXXReflectExpr *E) { case ReflectionKind::BaseSpecifier: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: + case ReflectionKind::Attribute: llvm_unreachable("reflect expression should not have this reflection kind"); } llvm_unreachable("invalid reflection"); diff --git a/clang/test/Reflection/info-equality.cpp b/clang/test/Reflection/info-equality.cpp index 75a959d0741b1..7e98cf4ebe789 100644 --- a/clang/test/Reflection/info-equality.cpp +++ b/clang/test/Reflection/info-equality.cpp @@ -140,6 +140,13 @@ static_assert(^^:: != ^^::myns); static_assert(^^::myns != ^^NsAlias); static_assert(^^::myns != ^^::myns::inner); +// Check equality of attributes +static_assert(^^[[nodiscard]] == ^^[[nodiscard]]); +static_assert(^^[[nodiscard("test")]] != ^^[[nodiscard]]); +static_assert(^^[[nodiscard("test")]] == ^^[[nodiscard("test")]]); +static_assert(^^[[nodiscard("test")]] != ^^[[nodiscard("another test")]]); +static_assert(^^[[nodiscard]] != ^^[[deprecated]]); + constexpr int i = 42; constexpr auto i_refl = ^^i; constexpr auto i_refl_copy = i_refl; diff --git a/libcxx/include/experimental/meta b/libcxx/include/experimental/meta index 1a1b322cc88d2..a4c5f78ff3799 100644 --- a/libcxx/include/experimental/meta +++ b/libcxx/include/experimental/meta @@ -381,6 +381,10 @@ template consteval auto is_annotation(info) -> bool; consteval auto annotate(info) -> info; +// Attributes reflection (P3385) +consteval auto is_attribute(info) -> bool; +consteval auto attributes_of(info r) -> vector; + } // namespace reflection_v2 } // namespace meta } // namespace std @@ -538,6 +542,10 @@ enum : unsigned { __metafn_get_ith_annotation_of, __metafn_is_annotation, __metafn_annotate, + + // P3385 attributes reflection + __metafn_get_ith_attribute_of, + __metafn_is_attribute, }; consteval auto __workaround_expand_compiler_builtins(info type) -> info; @@ -748,6 +756,25 @@ struct front_annotation_of { } }; +// TODO #ifdef P3385 feature flag + +struct front_attribute_of { + consteval info operator()(info reflectedEntity) const { + return __metafunction(detail::__metafn_get_ith_attribute_of, + reflectedEntity, LIFT(sentinel), 0); + } +}; + +struct next_attribute_of { + consteval info operator()(auto /*currItrInfo */, info reflectedEntity, + size_t idx) const { + return __metafunction(detail::__metafn_get_ith_attribute_of, + reflectedEntity, LIFT(sentinel), idx); + } +}; + +// TODO #endif P3385 feature flag + } // namespace __range_of_infos // ----------------------------------------------------------------------------- @@ -2174,6 +2201,24 @@ consteval auto annotate(info entity, info val) -> info { #endif // __has_feature(annotation_attributes) +// #ifdef TODO Feature flag for P3385 + +// Returns whether the reflected entity is an attribute. +consteval auto is_attribute(info r) -> bool { + return __metafunction(detail::__metafn_is_attribute, r); +} + +consteval auto attributes_of(info r) -> vector { + using iterator = + __range_of_infos::iterator<__range_of_infos::front_attribute_of, + __range_of_infos::next_attribute_of, + __range_of_infos::map_identity_fn>; + using range = __range_of_infos::range; + auto rng = range{r}; + return vector{rng.begin(), rng.end()}; +} + +// #endif TODO Feature flag for P3385 template [[deprecated("separated into 'reflect_value', 'reflect_result', and " diff --git a/libcxx/test/std/experimental/reflection/p3385-function-attributes.pass.cpp b/libcxx/test/std/experimental/reflection/p3385-function-attributes.pass.cpp new file mode 100644 index 0000000000000..786a2277f236b --- /dev/null +++ b/libcxx/test/std/experimental/reflection/p3385-function-attributes.pass.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// Copyright 2024 Bloomberg Finance L.P. +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 || c++11 || c++14 || c++17 || c++20 +// ADDITIONAL_COMPILE_FLAGS: -freflection +// ADDITIONAL_COMPILE_FLAGS: -freflection-new-syntax +// ADDITIONAL_COMPILE_FLAGS: -fattribute-reflection + +// +// +// [reflection] + +#include + +[[nodiscard]] int func(); + +consteval bool testAttributesOfFunc() { + static_assert(attributes_of(^^func).size() == 1); + static_assert(identifier_of(attributes_of(^^func)[0]) == "nodiscard"); + static_assert(attributes_of(^^func)[0] == ^^[[nodiscard]]); + return true; +} + +int main() +{ + static_assert(testAttributesOfFunc()); +} \ No newline at end of file