[llvm-bugs] [Bug 38398] New: Missing const when denoting type of name of copy-captured entity in unevaluated compound expression
via llvm-bugs
llvm-bugs at lists.llvm.org
Tue Jul 31 18:37:57 PDT 2018
https://bugs.llvm.org/show_bug.cgi?id=38398
Bug ID: 38398
Summary: Missing const when denoting type of name of
copy-captured entity in unevaluated compound
expression
Product: clang
Version: trunk
Hardware: PC
OS: Linux
Status: NEW
Severity: normal
Priority: P
Component: C++
Assignee: unassignedclangbugs at nondot.org
Reporter: eracpp at eml.cc
CC: dgregor at apple.com, llvm-bugs at lists.llvm.org
There are certain cases when Clang fails to apply const qualifiers when
denoting the type of a copy-captured entity within a lambda expression with no
`mutable` specifier.
Consider the following (https://godbolt.org/g/Tne4Qa):
----
template <typename T> T f(T&& x);
int main() {
int _x = 0;
int& x = _x;
[=]{
decltype((x)) v = x; // OK
decltype(f(x)) v = x; // !!
decltype((void(), x)) v = x; // !!
}();
}
----
The type of the expression `x` in these decltype specifiers should be that of
a class member access expression, as specified in [expr.prim.id.unqual]-2:
If the entity is a local entity and naming it from outside of an unevaluated
operand within the declarative region where the unqualified-id appears would
result in some intervening lambda-expression capturing it by copy, the type
of the expression is the type of a class member access expression naming the
non-static data member that would be declared for such a capture in the
closure object of the innermost such intervening lambda-expression.
As the lambda expression does not have a `mutable` specifier, the lambda
expression's `operator()` is a const member function. Therefore, the types
denoted should all be `int const&`, as specified by [dcl.type.simple]-4.5:
For an expression e, the type denoted by decltype(e) is defined as follows:
(...) - ...
(4.3) - otherwise, if e is an unparenthesized id-expression or an
unparenthesized class member access, decltype(e) is the type of the
entity named by e. If there is no such entity, or if e names a set of
overloaded functions, the program is ill-formed;
(...) - ...
(4.5) - otherwise, if e is an lvalue, decltype(e) is T&, where T is the type
of e;
However, if we examine the AST generated for each of the decltype specifiers by
the following code (note that the type of `x` has been changed to `int&&` to
help differentiate between the type of the entity`int&&` and the type of the
class member access expressions, both correct `int const&` and incorrect
`int&`):
----
template <typename T> T f(T&& x);
int main() {
int&& x = 0;
[=]{
using A = decltype(x);
using B = decltype((x));
using C = decltype(f(x));
using D = decltype((void(), x))
}();
}
----
We can see that the `QualType 'const int' const` child node for
`LValueReferenceType` present for B is missing in the case of C and D
(https://godbolt.org/g/G2wtQE):
DeclStmt <line:7:5, col:26>
`-TypeAliasDecl <col:5, col:15> col:11 A 'decltype(x)':'int &&'
`-DecltypeType 'decltype(x)' sugar
|-DeclRefExpr <col:24> 'int' lvalue Var 0x56032ea514f0 'x' 'int &&'
`-RValueReferenceType 'int &&'
`-BuiltinType 'int'
DeclStmt <line:8:5, col:28>
`-TypeAliasDecl <col:5, col:15> col:11 B 'decltype((x))':'const int &'
`-DecltypeType 'decltype((x))' sugar
|-ParenExpr <col:24, col:26> 'int' lvalue
| `-DeclRefExpr <col:25> 'int' lvalue Var 0x56032ea514f0 'x' 'int &&'
`-LValueReferenceType 'const int &'
`-QualType 'const int' const
`-BuiltinType 'int'
DeclStmt <line:9:5, col:29>
`-TypeAliasDecl <col:5, col:15> col:11 C 'decltype(f(x))':'int &'
`-DecltypeType 'decltype(f(x))' sugar
|-CallExpr <col:24, col:27> 'int' lvalue
| |-ImplicitCastExpr <col:24> 'int &(*)(int &)' <FunctionToPointerDecay>
| | `-DeclRefExpr <col:24> 'int &(int &)' lvalue Function 0x56032ea7bf40
| | 'f' 'int &(int &)' (FunctionTemplate 0x56032ea512e0 'f')
| `-DeclRefExpr <col:26> 'int' lvalue Var 0x56032ea514f0 'x' 'int &&'
`-LValueReferenceType 'int &'
`-BuiltinType 'int'
DeclStmt <line:10:5, col:36>
`-TypeAliasDecl <col:5, col:15> col:11 D 'decltype((void() , x))':'int &'
`-DecltypeType 'decltype((void() , x))' sugar
|-ParenExpr <col:24, col:34> 'int' lvalue
| `-BinaryOperator <col:25, col:33> 'int' lvalue ','
| |-CXXScalarValueInitExpr <col:25, col:30> 'void'
| `-DeclRefExpr <col:33> 'int' lvalue Var 0x56032ea514f0 'x' 'int &&'
`-LValueReferenceType 'int &'
`-BuiltinType 'int'
Clang incorrectly denotes the type of the expression `x` in case C and D as
`int&`. It correctly identifies the expression as a class member access
expression (otherwise it would denoted the type as `int&&` as it did in case A,
according to [expr.prim.lambda.capture]-11 and [dcl.type.simple]-4.3), but
fails to correctly apply [expr.ref]-4.2:
If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue; the
type of E1.E2 is T. Otherwise, one of the following rules applies.
- If E2 is a non-static data member and the type of E1 is “cq1 vq1 X”, and
the type of E2 is “cq2 vq2 T”, the expression designates the named member
of the object designated by the first expression. If E1 is an lvalue, then
E1.E2 is an lvalue; otherwise E1.E2 is an xvalue. Let the notation vq12
stand for the “union” of vq1 and vq2; that is, if vq1 or vq2 is volatile,
then vq12 is volatile. Similarly, let the notation cq12 stand for the
“union” of cq1 and cq2; that is, if cq1 or cq2 is const, then cq12 is
const. If E2 is declared to be a mutable member, then the type of E1.E2 is
“vq12 T”. If E2 is not declared to be a mutable member, then the type of
E1.E2 is “cq12 vq12 T”.
Stranger still, the use of `decltype(auto)` seems to apply the correct
behavior, even when the semantics of `decltype(auto) v = e;` should be
identical to `decltype(e) v = e;`, according to [dcl.type.auto.deduct]:
Placeholder type deduction is the process by which a type containing a
placeholder type is replaced by a deduced type. A type T containing a
placeholder type, and a corresponding initializer e, are determined as
follows: ... If the placeholder is the decltype(auto) type-specifier, T shall
be the placeholder alone. The type deduced for T is determined as described
in [dcl.type.simple], as though e had been the operand of the decltype.
Here is a comprehensive test case (https://godbolt.org/g/wDwMNS):
template <typename, typename> constexpr bool same = false;
template <typename T> constexpr bool same<T, T> = true;
template <typename T> T f(T&& t) { return static_cast<T&&>(t); }
int main() {
int&& x = 0;
[=]{
using X = decltype(x);
using Y = decltype((x));
using Z = decltype(f(x));
using T = decltype((void(), x));
static_assert(same<X, int&&>, "");
static_assert(same<Y, int const&>, "");
static_assert(same<Z, int const&>, ""); // !!
static_assert(same<T, int const&>, ""); // !!
decltype(auto) a = (x);
decltype(auto) b = f(x);
decltype(auto) c = (void(), x);
using A = decltype(a);
using B = decltype(b);
using C = decltype(c);
static_assert(same<A, int const&>, "");
static_assert(same<B, int const&>, ""); // OK?
static_assert(same<C, int const&>, ""); // OK?
}();
}
--
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20180801/cf0d3b56/attachment.html>
More information about the llvm-bugs
mailing list