https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122483
--- Comment #20 from Richard Sandiford <rsandifo at gcc dot gnu.org> --- (In reply to H.J. Lu from comment #17) > > Code like: > > > > void bar(void); > > void (*foo)(void) [[arm::streaming_compatible]] = bar; > > > > is a hard error [https://godbolt.org/z/b7GTo6nPx], so it doesn't IMO make > > sense for: > > > > void bar(void); > > > > to be treated as a redeclaration of: > > > > void bar(void) [[arm::streaming_compatible]]; > > [...] > > For x86, I got > > [hjl@gnu-tgl-3 gcc]$ cat x.c > extern void bar(void); > void (*foo)(void) __attribute__ ((preserve_none)) = bar; > [hjl@gnu-tgl-3 gcc]$ ./xgcc -B./ -S -O2 x.c > x.c:2:53: error: initialization of ‘void (__attribute__((preserve_none)) > *)(void)’ from incompatible pointer type ‘void (*)(void)’ > [-Wincompatible-pointer-types] > 2 | void (*foo)(void) __attribute__ ((preserve_none)) = bar; > | ^~~ > x.c:1:13: note: ‘bar’ declared here > 1 | extern void bar(void); > | ^~~ > [hjl@gnu-tgl-3 gcc]$ > > since ix86_comp_type_attributes returns 0: > > if (ix86_type_no_callee_saved_registers_p (type1) > != ix86_type_no_callee_saved_registers_p (type2)) > return 0; > > Does aarch64_comp_type_attributes check [[arm::streaming_compatible]]? Yeah, it does. That was my point :) We do still get the required error for the assignment (see the link above). But that means that the types are distinct, so it seems wrong to allow the same function to be declared with two different types. In response to the later comments: I'm not saying that anything else has to behave like the SVE ACLE attributes. I'm just asking that we don't regress the SVE ACLE attributes as part of other changes. I suppose the rationale for merging: void sc_a () [[arm::streaming_compatible]]; void sc_a (); is that the absence of an attribute is the absence of a choice. But that isn't true for SVE ACLE function types. Every function type is classified as either "normal", "streaming", or "streaming compatible". For obvious reasons, we didn't retrospectively require all "normal" functions to be annotated in a special way. That would have been completely impractical. Instead, the absence of an annotation indicates a normal function. But the point is that a choice is always being made. The choice is silent for normal functions and noisy for streaming and streaming-compatible functions. But there's a choice nonetheless. There is no concept of an "uncertain", "non-commital" or "deferential" type. I suppose another way of viewing it is that, in an SVE context: void f(void); should be read as equivalent to: void f(void) [[arm::nonstreaming]]; for something hypothetical attribute [[arm::nonstreaming]]. The error is then complaining about the conflict between "arm::nonstreaming" and arm::streaming_compatible. Another argument for why it's important is that the two types can be distinguished by _Generic and by C++ overloading. Consider: ----------------------------------------------------------------------------- extern void foo(void) [[arm::streaming_compatible]]; extern void bar(void); #define CLASSIFY(X) (_Generic(X, void (*)(void) [[arm::streaming_compatible]]: 1, void (*)(void): 2, default: 3 )) int foo_class = CLASSIFY(foo); int bar_class = CLASSIFY(bar); ----------------------------------------------------------------------------- This (correctly) sets foo_class to 1 and bar_class to 2 [https://godbolt.org/z/qGn3PveT9]. Similarly, in C++: ----------------------------------------------------------------------------- void foo(void) [[arm::streaming_compatible]]; void bar(void); constexpr int classify(void (*)(void) [[arm::streaming_compatible]]) { return 1; } constexpr int classify(void (*)(void)) { return 2; } int foo_class = classify(foo); int bar_class = classify(bar); ----------------------------------------------------------------------------- [https://godbolt.org/z/P6YTqWEWb]. Templates instantiated with one type are distinct from templates instantiated with the other. (Note that G++ doesn't yet implement the mangling correctly AFAIK, but Clang does.) So this isn't just something that affects the formal type system (the errors above) and implementation details (the ABI). It affects the abstract machine as well. As far as Clang comparisons go, Clang gets the SVE ACLE behaviour right in both C [https://godbolt.org/z/h4YnseMbP] and C++ [https://godbolt.org/z/4sz3e8K5W]. The code in both cases is: void foo(void) __arm_streaming_compatible; void foo(void); As can be seem from this, the ACLE syntax is actually: __arm_streaming_compatible rather than: [[arm::streaming_compatible]] to cope with concerns about using [[...]] attributes for things that affect behaviour/correctness. In GCC, the former is simply a macro that expands to the latter.
