cplusplus / CWG

Core Working Group
23 stars 7 forks source link

CWG2777 [temp.param.note] p3 lacks the formal wording to define the type of the id-expression denoting the template parameter object #374

Closed xmh0511 closed 5 months ago

xmh0511 commented 1 year ago

Full name of submitter (unless configured in github; will be published with the issue): Jim X

[temp.param.note] p3 says:

[Note 3: If an id-expression names a non-type non-reference template-parameter, then it is a prvalue if it has non-class type. Otherwise, if it is of class type T, it is an lvalue and has type const T ([expr.prim.id.unqual]). — end note]

Then, in [expr.prim.id.unqual], there is still only a note that says the type of the id-expression, [expr.prim.id.unqual.note] p4

[Note 4: If the entity is a template parameter object for a template parameter of type T ([temp.param]), the type of the expression is const T. — end note]

If there is no extra formal rule, then [temp.param] p8 just trumps any note, which says:

An id-expression naming a non-type template-parameter of class type T denotes a static storage duration object of

T is the declared type of the id-expression.

Suggested Resolution

When an id-expression that denotes the template parameter object appears in a context other than decltype(id-expression) , we should define its type with a formal rule.

In this decltype(id-expression), GCC and Clang have divergence, GCC says the type is const T while Clang says it is T.

struct A{};
template<auto const a>
void show(){
   decltype(a) b;
   A& rf = b;
}
int main(){
    show<A{}>();
}

https://godbolt.org/z/9jqhYn3vd

frederick-vs-ja commented 1 year ago

It seems that const T are normatively specified by [temp.param] p8 and [expr.prim.id.unqual] p3.

[temp.param] p8 states (emphasis mine):

An id-expression naming a non-type template-parameter of class type T denotes a static storage duration object of type const T, known as a template parameter object, [...]

So the id-expression denotes the template parameter object. Per [expr.prim.id.unqual] p3, the result of the id-expression is that template parameter object, and the type of the id-expression is that of the template parameter object, i.e., const T. (The id-expression is also specified to be an lvalue in [expr.prim.id.unqual] p3.)

Perhaps the current wording is a bit ambiguous. In [temp.param] p8, "of class type T" should be associated to "a non-type template-parameter", not "an id-expression".


In this decltype(id-expression), GCC and Clang have divergence, GCC says the type is const T while Clang says it is T.

struct A{};
template<auto const a>
void show(){
   decltype(a) b;
   A& rf = b;
}
int main(){
    show<A{}>();
}

The template parameter is of type A in this example. The top-level const is dropped ([temp.param] p6). So gcc is buggy here, which is known (GCC Bug 99631).

xmh0511 commented 1 year ago

"denote" in [expr.prim.id.unqual] p3 merely means it denotes the entity that is introduced by the declaration that declares the name(i.e. id-expression) for which name lookup finds the declaration, which is statically determined. Since the id-expression is introduced by the parameter-declaration(i.e. A a), its declared type should be A.

Moreover, "denote an object" does not mean the lvalue is required to be that type of object, for example:

int a;
const int& ref = a;

According to [expr.type] p1

The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.

The object denoted by the reference is a non-const object, by your logic, the id-expression ref thereof has type int? Furthermore, consider this example:

int a = 0;
using Type = int const;
new (&a) Type{1};

Now, the name can arguably say it denotes a const object, so the type of the id-expression is const it? Presumably, it is not the intent. The type of a name and the type of object it denotes does not have a close connection.

So, [expr.prim.id.unqual] p3 merely mean the type of an id-expression is the declared type of the declaration introducing the name.

frederick-vs-ja commented 1 year ago

"denote" in [expr.prim.id.unqual] p3 merely means it denotes the entity that is introduced by the declaration that declares the name(i.e. id-expression) for which name lookup finds the declaration, which is statically determined.

But a template parameter is arguably not itself an entity (although the term "value" in [basic.pre] p3 is not very clear), while a template parameter object is definitely an entity.

Moreover, "denote an object" does not mean the lvalue is required to be that type of object, for example:

int a;
const int& ref = a;

Hmm... it is far less than ideal to use the ambiguous verb "denote", which you should have noticed for many times (cplusplus/draft#5346).

While "denote" is ambiguous, I think we can say a template parameter is not a thing that can be denoted by an id-expression (in the meaning of denoting an entity), so [temp.param] p8 should be considered unambiguously meaning that the id-expression only denotes the template parameter object, and thus have the same type as that object.

I think we should have an omnibus issue that clarifies "denoting", "naming", "designating", and "referring to".

xmh0511 commented 1 year ago

But a template parameter is arguably not itself an entity (although the term "value" in [basic.pre] p3 is not very clear), while a template parameter object is definitely an entity.

The template parameter introduces an object that is an entity, whose declaration is a parameter-declaration, and if there is any other specification, the type of the parameter-declaration is specified by [dcl.meaning], regardless of what actual object it denotes. The declaration just declares the name should denote a certain object, rather than specify which object it denotes.

So, you shouldn't determine the type of the expression according to the type of the object it denotes, instead, there is no special specification, the type of the name/expression should be determined by its declaration.

frederick-vs-ja commented 1 year ago

The template parameter introduces an object that is an entity

Not obviously true to me. I think it's still unclear whether a template parameter object is introduced by a template parameter. Perhaps we can say a template parameter object is introduced and created in some way other than declaration (there're some other objects created in unclear ways, see CWG2334).

IMO a template parameter object is very similar to a variable, but the current wording seemingly intends to distinguish between a template parameter object and a variable. (Perhaps we should continue the discussion in #148.)

xmh0511 commented 1 year ago

A declaration that introduces an object does not necessarily mean the declaration creates the object. Consider this example:

extern A a; // introduces an object but not create it. 

IMO a template parameter object is very similar to a variable

The variable is a unified name for either a reference or an object, even though the definition is unclear for what can be called a declaration of an object, however, this is another recorded issue.


As you mentioned above, we first should clear the definition of these verbs: denote, designate, which was discussed in https://github.com/cplusplus/CWG/issues/44#issuecomment-1144135736

When we say a name/expression denotes an entity, it should mean after performing a name lookup, which finds a declaration, that introduces the name, introduces such an entity, i.e. it is a declaration introducing such a kind of entity.

When we say a name/expression designates an object, we intend to mean, it is identified as a specific object, after evaluating it.

jensmaurer commented 1 year ago

CWG2777

Note that decltype is handled specially for non-type template parameters.