Skip to content

P3385 #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 38 commits into from
Closed

P3385 #102

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
2a2b29e
Stash clueless hacks
Oct 6, 2024
4a28ad2
Stash
Oct 6, 2024
a2f0785
Stash
Oct 6, 2024
56fc53e
Stash
Oct 6, 2024
4b439c0
Stash
Oct 6, 2024
edef0d2
Stash diagnostic to trace compilation
Oct 6, 2024
0c32b98
Remove misplaced revert on attribute parse
Oct 7, 2024
9dd6bae
Add dumb trace for the boys
Oct 7, 2024
fe573e4
Clean up brkpoint traces
Oct 7, 2024
b0d1f58
Stash non working meta functions
Oct 8, 2024
7d12469
Support is_attribute
Oct 12, 2024
e2f916b
Cleanup
Oct 12, 2024
a824da2
Cleanup
Oct 12, 2024
2fe287d
Stash early ith attributes of
Oct 13, 2024
0a1288a
Stash
Oct 13, 2024
c6b54dd
Stash attributesOf
Oct 13, 2024
75be9e4
Stash attributesOf
Oct 13, 2024
ff97c21
Merge branch 'attrCommonInfo' into p3385
Oct 14, 2024
424e828
Stash to check regression
Oct 20, 2024
ef2ff0f
Stash to check regression
Oct 21, 2024
13be7da
Pass easy test on attribute_of type
Oct 21, 2024
a3cbe57
Stash interim splice
Oct 27, 2024
9c0c904
Stash interim splice
Oct 27, 2024
83ff0f3
Stash interim splice
Oct 27, 2024
59135b3
Support some splice, note that attributes_of(^^[[ is buggy from this …
Oct 27, 2024
a7539bc
Shame stash the parsedAttrs with Parser to deal with lifetime...
Nov 13, 2024
d7d903f
Stash splicing type reflection
Nov 25, 2024
85fbac6
Fix has_identifier
Dec 8, 2024
926717e
Add flag
Jan 5, 2025
7e75e39
Fix new flag
Jan 5, 2025
1fa57fb
Use ParsedAttr not AttrCommonInfo
Jan 5, 2025
ee70556
Recover arg when splicing an attribute reflection inside [[]]
Feb 2, 2025
ed5c795
Refactor some of the cx11 only handlind
Mar 2, 2025
9080507
equality operator for attributes reflection
changkhothuychung Mar 13, 2025
f8baccc
Merge pull request #2 from changkhothuychung/p3385
zebullax Mar 15, 2025
1347cba
attributes on function
changkhothuychung Mar 25, 2025
a2df87f
Merge pull request #3 from changkhothuychung/p3385
zebullax Mar 25, 2025
156110a
Dependent spliced attributes (#4)
zebullax Apr 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions P3385.md
Original file line number Diff line number Diff line change
@@ -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<IdentifierInfo*>(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()`
6 changes: 6 additions & 0 deletions clang/include/clang/AST/APValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ template <typename T> class BasicReaderBase;
class Expr;
class FieldDecl;
class NamespaceDecl;
class ParsedAttr;
struct PrintingPolicy;
class Type;
class ValueDecl;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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");
Expand Down
15 changes: 14 additions & 1 deletion clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class ASTContext;
class AttributeCommonInfo;
class FunctionDecl;
class OMPTraitInfo;
class ParsedAttr;

/// Attr - This represents one attribute.
class Attr : public AttributeCommonInfo {
Expand All @@ -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'.");
}
Expand All @@ -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<attr::Kind>(AttrKind); }
Expand All @@ -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;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Module;
class NamedDecl;
class ObjCContainerDecl;
class ObjCMethodDecl;
class ParsedAttr;
struct PrintingPolicy;
class RecordDecl;
class SourceManager;
Expand Down Expand Up @@ -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<attr_iterator>;
Expand Down
7 changes: 4 additions & 3 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<bool(APValue &, const Expr *,
bool ConvertToRValue)>;

// 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<PartialDiagnostic &(SourceLocation,
unsigned)>;

Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -2991,6 +2991,7 @@ DEF_TRAVERSE_STMT(CXXReflectExpr, {
case ReflectionKind::Namespace:
case ReflectionKind::BaseSpecifier:
case ReflectionKind::DataMemberSpec:
case ReflectionKind::Attribute: // TODO P3385 ?
break;
}
}
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/AST/Reflection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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'
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>];
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticFrontendKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang/Basic/DiagnosticMetafnKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down Expand Up @@ -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">;
Expand Down
18 changes: 18 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">,
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
5 changes: 5 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -3504,6 +3504,11 @@ defm parameter_reflection : BoolFOption<"parameter-reflection",
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Enable proposed parameter reflection as described by P3096">,
NegFlag<SetFalse>>;
defm attribute_reflection : BoolFOption<"attribute-reflection",
LangOpts<"AttributeReflection">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
"Enable proposed parameter reflection as described by P3385">,
NegFlag<SetFalse>>;
defm expansion_statements : BoolFOption<"expansion-statements",
LangOpts<"ExpansionStatements">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CC1Option],
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Parse/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ class Parser : public CodeCompletionHandler {

/// Factory object for creating ParsedAttr objects.
AttributeFactory AttrFactory;
/// FIXNE P3385
ParsedAttributes Attrs;

/// Gathers and cleans up TemplateIdAnnotations when parsing of a
/// top-level declaration is finished.
Expand Down Expand Up @@ -3047,6 +3049,11 @@ class Parser : public CodeCompletionHandler {
ParseCXX11AttributeSpecifierInternal(Attrs, OpenMPTokens, EndLoc);
ReplayOpenMPAttributeTokens(OpenMPTokens);
}

bool tryParseSpliceAttrSpecifier(ParsedAttributes &Attrs,
SourceLocation *EndLoc = nullptr);
/// Try parsing a splice expression when inside an attribute specifier

void ParseCXX11Attributes(ParsedAttributes &attrs);
/// Parses a C++11 (or C23)-style attribute argument list. Returns true
/// if this results in adding an attribute to the ParsedAttributes list.
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/AttrSubjectMatchRules.h"
#include "clang/Basic/AttributeCommonInfo.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/CapturedStmt.h"
#include "clang/Basic/Cuda.h"
Expand Down Expand Up @@ -15173,6 +15174,7 @@ class Sema final : public SemaBase {
ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc,
ParsedTemplateArgument Template);
ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, CXXSpliceExpr *E);
ExprResult ActOnCXXReflectExpr(SourceLocation OpLoc, ParsedAttr *a);

ExprResult ActOnCXXMetafunction(SourceLocation KwLoc,
SourceLocation LParenLoc,
Expand Down Expand Up @@ -15222,6 +15224,7 @@ class Sema final : public SemaBase {
ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc,
SourceLocation OperandLoc,
TemplateName Template);
ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, ParsedAttr *A);

// Reflection of expression operands.
ExprResult BuildCXXReflectExpr(SourceLocation OperatorLoc, Expr *E);
Expand Down
Loading