Skip to content
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

[dcl.constexpr] A full-expression of initialization of a variable may be not an expression #4852

Open
frederick-vs-ja opened this issue Sep 1, 2021 · 7 comments

Comments

@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented Sep 1, 2021

Originally posted by @xmh0511 in #4837 (comment) and #4798.

Currently [dcl.constexpr] p10 says that in the defintion of a constexpr variable, the "full-expression of the initialization" shall be a constant expression. And according to [expr.const] p1, a constant expression is also an expression.

However, the "full-expression of the initialization" of a variable may be not an expression. It seems that "full-expression of the initialization" of a variable corresponds to the case specified in [intro.execution] p5 (5.4): an init-declarator (or a mem-initializer) is not an expression.

Note that [expr.const] p2 (2.2) uses "when interpretd as", which may imply that the "full-expression of its initialization" is not itself an expression.

Perhaps there are some contradictions currently, and we should clarify that

  • the full-expression of initialization of a variable is not itself an expression (and hence not a constant expression), and
  • how to "interpret" an initialization as an expression.
@xmh0511
Copy link
Contributor

xmh0511 commented Sep 1, 2021

Actually, the concern in #4798 arises from certain examples

struct T{
  constexpr T(Class const& rf){}  // #1
};
constexpr T const& rf = T{Class()};

In this full-expression of the initialization, it will comprise the call of #1, the default constructor call of Class, and the destructor call of Class at the end of that full-expression and that full-expression of the initialization should be a glvaue constant expression for this purpose.

The purpose of "when interpreted as constant-expression" is that we give a hypothetical grammar meaning to "full-expression of the initialization" that should have been not an expression on grammar, in order to make all semantic rules that apply to the expression on grammar apply to "full-expression of the initialization". “full-expression ” works for collecting these separated expressions.

We lack the wording about the storage duration of the temporary variable in this full-expression of initialization, as specified in #4840.

@xmh0511
Copy link
Contributor

xmh0511 commented Sep 2, 2021

BTW, since @jensmaurer in #4837 has answered like this

In your example above, the value category is only relevant for the expressions 0 and A(0) (and those value categories are well-specified); everywhere else, we don't syntactically have an expression and we never ask for the value category.

the full-expression of the initialization is the exception, I try to adjust the example above that comment as the following

struct A{
  constexpr A() = default;
  constexpr A(int){}
};
constexpr A t = A(0);  //#1

The specifier constexpr in the variable declaration requires that the full-expression of the initialization is a constant expression, even though it is not a syntactical expression, and we impose it to interpret as a constant-expression that is syntactical, however, what's the value category of this hypothetical constant-expression? It seems to lack to expound. I think the value category is significant here since a constant expression is either a glvalue or prvalue core constant expression, we require to do further checks according to their value category.

@frederick-vs-ja
Copy link
Contributor Author

frederick-vs-ja commented Sep 5, 2021

Proposed changes:

Change [dcl.constexpr] p10:
from

... In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression ([expr.const]). ...

to

... In any constexpr variable declaration, the variable shall be constant-initialized ([expr.const]). ...

Change [expr.const] p2 (2.2):
from

the full-expression of its initialization is a constant expression when interpreted as a constant-expression, except that if o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.
[Note 2: Such a class can have a non-trivial destructor. Within this evaluation, std​::​is_­constant_­evaluated() ([meta.const.eval]) returns true. — end note]

to

the execution of the full-expression of its initialization satisfies the constraints of a core constant expression, without requiring that it is an expression and with “the evaluation of E” replaced by the execution, and either o is a reference that refers to an entity that is a permitted result of a constant expression (as defined below), or the value of o satisfies the constraints of a prvalue core constant expression that is also a constant expression.
[Note ?: If o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types. — end note]
[Note ?: Such a class can have a non-trivial or non-constexpr destructor. Within this evaluation, std​::​is_­constant_­evaluated() ([meta.const.eval]) returns true. — end note]

@xmh0511 @jensmaurer How do you think about these changes? Is a CWG issue needed?


Add following contents to show how to transform the constraints:

, without requiring that it is an expression and with “the evaluation of E” replaced by the execution

@frederick-vs-ja
Copy link
Contributor Author

Oh, initialization of an object that is neither variable nor temporary object can also satisfy these constraints, and such initialization can appear in constant evaluation since C++20.
Should we also specify that such an object is also constant-initialized? Is a (separated) CWG needed?

@xmh0511
Copy link
Contributor

xmh0511 commented Sep 6, 2021

the execution of the full-expression of its initialization satisfies the constraints of a core constant expression

I would say "full-expression of its initialization" is not an expression of the grammar, whether a construct is a core constant expression or not is first based on it is an expression of the grammar.

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following.

IMO, change the following normative rule

If o is an object, that full-expression may also invoke constexpr constructors for o and its subobjects even if those objects are of non-literal class types.

to be a note may be radical but it seems plausible. I think for any initialization of an object, the destructor call cannot appear in that full-expression, even though the object is temporary whose lifetime has been extended or not.

@frederick-vs-ja
Copy link
Contributor Author

I think the updated wording can show how to transform the constraints of a core constant expression.

@xmh0511
Copy link
Contributor

xmh0511 commented Feb 8, 2022

@jensmaurer @frederick-vs-ja

After thinking more about this issue. I think the point of clarifying this issue is:

  • what are the constituents of the constant-expression when interpreting a full-expression of initialization as a constant-expression?
  • what is the full-expression of initialization?

Consider a slightly complex example:

struct A{
  constexpr A(int){}
};
constexpr A&& rf = 0;  // #1

First, what is the full-expression of the initialization at #1? Is the full-expression the init-declarator, as per [intro.execution-5.4]? According to [intro.execution] p5

If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition.

And [dcl.init.ref#5.4.1]

The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference.

It seems that the language construct init-declarator can be considered as an expression for purposes of the definition of a full-expression. Name the language construct(init-declarator) as the expression E, since A::A(int) is implicitly called by E, thus the call of A::A(int) is a subexpression of E. [intro.execution] p5 also says

Conversions applied to the result of an expression in order to satisfy the requirements of the language construct in which the expression appears are also considered to be part of the full-expression.

According to [dcl.init.ref#5.3]

If the converted initializer is a prvalue, its type T4 is adjusted to type “cv1 T4” ([conv.qual]) and the temporary materialization conversion ([conv.rval]) is applied. In any case, the reference binds to the resulting glvalue (or to an appropriate base class subobject).

The temporary conversion is a part of the full-expression. Also, according to [expr.const] p2.2

the full-expression of its initialization is a constant expression when interpreted as a constant-expression

we can conclude that: E is interpreted as a constant-expression and E is a full-expression. The constituents of E comprise: the call of A::A(int) and temporary materialization conversion

It is my interpretation of full-expression of initialization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants