Unrecognized C++ attributes

Hi there,

I don’t know if this is right place, but I’m working on a tool to enforce a memory safe subset of C++. That memory safe subset needs custom C++ attributes (for lifetime annotation). My impression is that the current recommended way to implement custom attributes involves building and maintaining a custom version of libtooling (which the tool uses). That’s rather inconvenient, and for my purposes, not necessary I think. Currently, as a work-around hack, I just express the attribute as macro that “piggybacks” on an already supported attribute that takes an arbitrary string parameter. If you’re interested you can see it here:

So I’d like to propose some changes in the way clang handles attributes. First, stop warning on the presence of unrecognized attributes by default. Just because clang doesn’t recognize an attribute doesn’t mean it’s invalid. It could be used by another tool in the build chain.

Second, include unrecognized attributes in the AST along with the supported ones that get included in the AST, so that libtooling programs can take note of their presence. Even if only their text representation is provided.

If for some reason this isn’t acceptable, then as a second choice, I’d request that clang support a “generic” attribute that takes a string parameter. (And shows up in the AST.)

Thanks.

Noah

I don’t think we want to stop warning on ‘unknown attribute’, this catches an absurd number of typos/thinkos.

I don’t think we want to add unsupported attributes, that would make for a somewhat messy AST.

However, the ‘arbitrary string’ attribute you want is annotate: Compiler Explorer

Thank you. I suppose I can live with that.

I’d be strongly opposed to that change; we warn by default because the user wrote some code and the compiler is ignoring it. That’s especially relevant for vendor-specific attributes where the effects of ignoring the attribute are unknowable. This also helps to catch simple typos fairly often, as Erich mentions. Users who don’t want “unknown attribute” warnings have a flag they can use: -Wno-unknown-attributes.

We’ve explored this a little bit in the past, and if we find a reasonable way to support it, I’d be delighted. However, it’s a bit tricky because of attribute arguments. Attributes in the Clang AST are able to track other AST nodes like Expr * or Decl *, and they can track basic types like integers and strings. But vendor specific attributes can (and do) take arbitrary token soup that we have no way to represent in the AST easily. I think we could at least store the list of tokens in that case so that we have something to replay, but we’d still have to figure out how things like AST matchers work. But despite that, I’d love to see us support this.

You can use the annotate attribute from this, but are you aware that Clang supports plugin-based attributes as well? That might be another option for you: llvm-project/Attribute.cpp at main · llvm/llvm-project · GitHub

but are you aware that Clang supports plugin-based attributes as well?

Ok, that makes sense. But there would still be the warning issue. I’d still like for people to be able to use my attributes as an “enhancement” to their code that can safely be ignored by any C++ compiler. Perhaps if we designated a standard namespace for safely ignorable attributes? Like while [[clang::unrecognized_attribute]] would cause a warning, [[optional::unrecognized_attribute]] wouldn’t?

I’d still like for people to be able to use my attributes as an “enhancement” to their code that can safely be ignored by any C++ compiler.

I sympathize with the goal, but I suspect getting all compilers to agree here will be hard without involving standards bodies.

Perhaps if we designated a standard namespace for safely ignorable attributes? Like while [[clang::unrecognized_attribute]] would cause a warning, [[optional::unrecognized_attribute]] wouldn’t?

The trouble there is that optional is a vendor specified namespace that could be validly used by another vendor, so we can’t really stomp on that namespace safely.

However, one idea that Erich and I have been discussing off-list a bit is to perhaps support something like -Wno-unknown-attribute=attribute_name. There may be some scaling issues (there are a lot of attributes, so what happens when you want to ignore 30 of them?) with it and there may be some extension issues with it (people may want to be able to specify which syntax to ignore the attribute name in, or which vendor namespace to ignore as a whole). But it seems like something along these lines could help your use case?

perhaps support something like -Wno-unknown-attribute=attribute_name .

Well, with my current (hacky) solution, you can add my “attributes” to any working code without incurring any warnings or needing to modify the build in any way. But rather than explicitly adding C++ or compiler specific attributes, you use use a macro that takes a string argument. If the code is being compiled with clang, the macro is defined as an attribute supported by clang. (Otherwise the macro is defined as nothing.)

The benefit being that there is no risk to adding my “annotations” to your code. It won’t break your build.

Now noticing that even __attribute__((annotate(""))) is not supported by other compilers, I don’t see how I can avoid using macros with compiler specific definitions anyway.

Idk, it’s just surprising that attributes, even the “modern” C++ attributes, are not portable. At all. Right?

The trouble there is that optional is a vendor specified namespace that could be validly used by another vendor

Ok, [[__optional__::unrecognized_attribute]] then? :slight_smile:

__optional__ is ALSO a vendor specified namespace. There are no values before the “::” that are both legal and not vendor specified Namespaces.

1 Like

This is the most common design pattern I see in the wild for using attributes.

That’s correct. Attributes are (at a high level) giving compile-time information to an implementation that can’t otherwise be easily expressed in syntax. They are inherently nonportable. This is why we standardized __has_cpp_attribute() and __has_c_attribute() for standard attributes (and support __has_attribute() for GNU-style attributes and __has_declspec_attribute() for __declspec attributes) – it allows programmers to write the macros you’re talking about.

1 Like

Good to know. So I won’t feel as sheepish about my “hacky” implementation. :slight_smile: Thanks for all the explanation.

1 Like