Can constexpr be combined with volatile?
Asked Answered
G

2

32

The following snippet works fine in Clang 3.5 but not in GCC 4.9.2:

int main()
{
    constexpr volatile int i = 5;
}

with error:

error: both 'volatile' and 'constexpr' cannot be used here

If I inspect the assembly that Clang generates, it shows 5 as expected:

movl    $5, -4(%rsp)

In GCC, constexpr int i = 5 is optimized away, but volatile int i = 5 also shows 5 in the assembly. volatile const int i = 5 compiles in both compilers. It's not a foreign concept for something to be both volatile and const at the same time.

Which compiler is correct by the standards?

Grumpy answered 5/3, 2015 at 17:2 Comment(17)
What is it you are trying to express? They are essentially opposites. A constant will never change once it has been set, but a volatile is all but guaranteed to change (possibly from another thread even).Shoulders
constexpr and const are not the sameHoral
@TheBuzzSaw: volatile doesn't mean anything has a tendency to change. it just means that the memory access can have side effects, and the compiler should therefore treat it as I/O. It can very well be necessary to use volatile on a system even when the value is guaranteed to stay constant.Handknit
@BЈовић constexpr implies const.Grumpy
@Grumpy Yes, but other way is not true. For example, try to create a constexpr std::string object.Horal
@Mehrda Yes. That is right. A volatile value does not have to change, but in typical usage, it often does. Hence the need to inform the compiler that it should resist performing optimizations on how it reads/writes to that particular value.Shoulders
@TheBuzzSaw: Thinking about it in terms of "changing behind your back" seems rather dangerous, because it suggests software can be changing the value, which would be rather alarming since the memory block does not have proper atomic and memory ordering semantics, so the program is already meaningless if the value is changing via another thread. On the other hand, if the value is changing via hardware, then how would you use that kind of reasoning on write-only variables? In that case you would be worried about the side-effects, not about compiler optimizations.Handknit
Looks like GCC 5 rejects this program as well. I'll go look for a bug report...Floozy
@Mehrdad If I were working with hardware-mapped values, I'd agree with you. You are describing its usage in a very particular domain. I'm referring to the common case of an isRunning boolean that can be altered from another thread. Marking it volatile ensures the compiler will not make a dumb decision regarding its value in other stages of the code.Shoulders
@Shoulders such a use of a Boolean as you describe is undefined behavior.Handknit
@Mehrdad Details? Links?Shoulders
@TheBuzzSaw: I'm too lazy to find a link for you, but trust me when I say that attempting to write to a variable that can be simultaneously read to or written from other threads is UB in C++. (Keep in mind that if you were never going to modify it then you would never be reading from it from any thread, since you would already know what the value would be.) You can find a ton of questions and answers on this if you search for them.Handknit
@Mehrdad I'll go research the matter. Can you think of examples of const volatile outside of embedded programming?Shoulders
@Shoulders volatile does not provide any synchronization. It may work under right cirumstances (suitable CPU etc), but it is not in any way guaranteed to work, so use atomics for that kind of inter-thread flag, unless you want to intentionally write bad code. You use const volatile when you want to only read volatile variable, and want a compiler error if you try to write to it (whatever changes the value would then need to somehow work around constness, of course).Pylos
@Shoulders E.g., const volatile might make sense for a read-only memory map of a file that another process is concurrently modifying, or read-only memory-mapped registers of a hardware device.Floozy
@TheBuzzSaw: I can't think of any, but Casey gave a great example.Handknit
@BЈовић: I can think of reasonable semantics and purpose: require that the expression be stored in the object file (or other code image) rather than having its value computed at runtime, but also require that code using the expression use the value stored in the code image. Such behavior could be important in cases where it may be necessary to have a program be able to patch its own executable to change a default value.Haubergeon
J
29

Yes, this is valid, there was defect report 1688: Volatile constexpr variables that was filed for this, saying:

There does not appear to be language in the current wording stating that constexpr cannot be applied to a variable of volatile-qualified type. Also, the wording in 5.19 [expr.const] paragraph 2 referring to “a non-volatile object defined with constexpr” might lead one to infer that the combination is permitted but that such a variable cannot appear in a constant expression. What is the intent?

it was rejected as not a defect(NAD), the response and rationale was:

The combination is intentionally permitted and could be used in some circumstances to force constant initialization.

As the DR points out such a variable is itself not usable in a constant expression:

constexpr volatile int i = 5;    
constexpr int y = i ;         // Not valid since i is volatile

Section [expr.const]/2 includes all the cases that makes a conditional-expression not a core constant expression including:

an lvalue-to-rvalue conversion (4.1) unless it is applied to

and all the exception require:

[...]that refers to a non-volatile [...] object [...]

Johnniejohnny answered 5/3, 2015 at 18:25 Comment(1)
I knew there had to be a DR, but couldn't find one. Well done.Floozy
F
19

Quoting N4140 [dcl.constexpr]/9:

A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized.

Literal type is defined in [basic.types]/10:

A type is a literal type if it is:

(10.1) — void; or

(10.2) — a scalar type; or

(10.3) — a reference type; or

(10.4) — an array of literal type; or

(10.5) — a class type (Clause 9) that has all of the following properties:

(10.5.1) — it has a trivial destructor,

(10.5.2) — it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor, and

(10.5.3) — all of its non-static data members and base classes are of non-volatile literal types.

Scalar type is in paragraph 9:

Arithmetic types (3.9.1), enumeration types, pointer types, pointer to member types (3.9.2), std::nullptr_t, and cv-qualified versions of these types (3.9.3) are collectively called scalar types.

int is arithmetic, so volatile int is a scalar type and hence a literal type. constexpr volatile int i = 5; is thus a well-formed declaration.

Interestingly, an expression that evaluates i cannot be a core-constant-expression since it applies an lvalue-to-rvalue conversion to a glvalue of volatile type ([expr.const]/2). Consequently, expressions that evaluate i are neither integral constant expressions nor constant expressions. I'm not sure that the constexpr in that declaration has any effect beyond making i implicitly const, and (nod to @T.C.) requiring its initializer to be a constant expression.

I've reported this as GCC bug 65327, we'll see what the GCC folks have to say.

2015-03-16 Update: Bug has been fixed for GCC 5.

Floozy answered 5/3, 2015 at 17:22 Comment(5)
The presence of "non-volatile" in (10.5.3) says to me that it's at least somewhat likely that the standard meant to exclude volatile-qualified types in general, and allowing this was an oversight.Avalon
"I'm not sure that the constexpr in that declaration has any effect beyond making i implicitly const." - It still constrains the initializer to be a constant expression.Heinous
For what it's worth, here's the patch that introduced this error. Testing on wandbox shows the error was introduced between 4.5.4 and 4.6.4.Grumpy
@Heinous indeed, this is the rationale the committee used in the defect report I mention in my answer. I knew there had to be a defect report for this.Johnniejohnny
@Grumpy That change was to allow const together with constexpr. Revision 166013 introduced the various constexpr checks, including the error when constexpr and volatile are used together.Floozy

© 2022 - 2024 — McMap. All rights reserved.