https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127301

            Bug ID: 127301
           Summary: `__attribute__((const))` can be better documented with
                    respect to memory access
           Product: gcc
           Version: 15.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: [email protected]
  Target Milestone: ---

```
// Build and run both ways:
//   g++ -std=gnu++23 -O3 -DATTR=const attr_const_dse.cc -o const && ./const
//   g++ -std=gnu++23 -O3 -DATTR=pure  attr_const_dse.cc -o pure  && ./pure
//
// Observed with g++ 15.2.1 20260512:
//   const -> 2.0746093145635613e-317      <-- wrong; subnormal built from
stack garbage
//   pure  -> 3                            <-- correct

#include <initializer_list>
#include <cstdio>

struct Elem {
    constexpr Elem() {}
    constexpr Elem(double x) noexcept : v(x) {}
    double v = 0.0;
};

[[nodiscard]] Elem __attribute__((ATTR)) __attribute__((noinline))
combine(std::initializer_list<Elem> xs, int k) {
    Elem acc(k ? 1e300 : -1e300);
    for (const Elem& e : xs)
        acc = (k ? (e.v < acc.v) : (e.v > acc.v)) ? e : acc;
    return acc;
}

__attribute__((noinline)) double caller(double a, double b, double c, int k) {
    return combine({Elem(a), Elem(b), Elem(c)}, k).v;
}

int main() { printf("%.17g\n", caller(1.0, 2.0, 3.0, 0)); }
```

Not a big deal, but I think the documentation can be made more clear here.

```
const

    This attribute applies to functions.

    Calls to functions whose return value is not affected by changes to the
observable state of the program and that have no observable effects on such
state other than to return a value may lend themselves to optimizations such as
common subexpression elimination. Declaring such functions with the const
attribute allows GCC to avoid emitting some calls in repeated invocations of
the function with the same argument values.

    For example,

    [[gnu::const]] int square (int);

    tells GCC that subsequent calls to function square with the same argument
value can be replaced by the result of the first call regardless of the
statements in between.

    The const attribute prohibits a function from reading objects that affect
its return value between successive invocations. However, functions declared
with the attribute can safely read objects that do not change their return
value, such as non-volatile constants.

    The const attribute imposes greater restrictions on a function’s definition
than the similar pure attribute. Declaring the same function with both the
const and the pure attribute is diagnosed. Because a const function cannot have
any observable side effects it does not make sense for it to return void.
Declaring such a function is diagnosed.

    Note that a function that has pointer arguments and examines the data
pointed to must not be declared const if the pointed-to data might change
between successive invocations of the function. In general, since a function
cannot distinguish data that might change from data that cannot, const
functions should never take pointer or, in C++, reference arguments. Likewise,
a function that calls a non-const function usually must not be const itself.
```

"Note that a function that has pointer arguments and examines the data pointed
to must not be declared const if the pointed-to data might change between
successive invocations of the function." This statement strongly implies that
if the pointed-to data *doesn't* change, then all is well. But that doesn't
seem to be the case as the example demonstrates.

The sentence after says "In general, since a function cannot distinguish data
that might change from data ...". It's not clear here what "distinguish" means.
I think it might be saying you can pass a `T*`(modifiable) into a `const T*`
parameter slot, and there's no way for the function to prevent it, but honestly
I'm not sure.

Moreover, there are C++ types like `std::initializer_list` or
`std::string_view` that pass pointers underneath.

I think that paragraph may be better rendered as "`const` functions should
never take pointers, or in C++, reference types, either directly, or
transitively through a complex data type argument." without the preceding
statements.

Of course, I may be misunderstanding something here.

Reply via email to