Skip to content

Commit e53dfbc

Browse files
committed
[Clang] Implement CWG2813
1 parent d63ade6 commit e53dfbc

File tree

10 files changed

+164
-51
lines changed

10 files changed

+164
-51
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,11 @@ Resolutions to C++ Defect Reports
260260
- Clang now requires a template argument list after a template keyword.
261261
(`CWG96: Syntactic disambiguation using the template keyword <https://cplusplus.github.io/CWG/issues/96.html>`_).
262262

263+
- Clang now allows calling explicit object member functions directly with prvalues
264+
instead of always materializing a temporary, meaning by-value explicit object parameters
265+
do not need to move from a temporary.
266+
(`CWG2813: Class member access with prvalues <https://cplusplus.github.io/CWG/issues/2813.html>`_).
267+
263268
C Language Changes
264269
------------------
265270

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9182,6 +9182,9 @@ def warn_unused_constructor : Warning<
91829182
def warn_unused_constructor_msg : Warning<
91839183
"ignoring temporary created by a constructor declared with %0 attribute: %1">,
91849184
InGroup<UnusedValue>;
9185+
def warn_discarded_class_member_access : Warning<
9186+
"left operand of dot in this class member access is discarded and has no effect">,
9187+
InGroup<UnusedValue>;
91859188
def warn_side_effects_unevaluated_context : Warning<
91869189
"expression with side effects has no effect in an unevaluated context">,
91879190
InGroup<UnevaluatedExpression>;

clang/lib/Sema/SemaExprMember.cpp

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,15 +1015,6 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
10151015
: !isDependentScopeSpecifier(SS) || computeDeclContext(SS)) &&
10161016
"dependent lookup context that isn't the current instantiation?");
10171017

1018-
// C++1z [expr.ref]p2:
1019-
// For the first option (dot) the first expression shall be a glvalue [...]
1020-
if (!IsArrow && BaseExpr && BaseExpr->isPRValue()) {
1021-
ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
1022-
if (Converted.isInvalid())
1023-
return ExprError();
1024-
BaseExpr = Converted.get();
1025-
}
1026-
10271018
const DeclarationNameInfo &MemberNameInfo = R.getLookupNameInfo();
10281019
DeclarationName MemberName = MemberNameInfo.getName();
10291020
SourceLocation MemberLoc = MemberNameInfo.getLoc();
@@ -1140,26 +1131,68 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
11401131
BaseExpr = BuildCXXThisExpr(Loc, BaseExprType, /*IsImplicit=*/true);
11411132
}
11421133

1134+
// C++17 [expr.ref]p2, per CWG2813:
1135+
// For the first option (dot), if the id-expression names a static member or
1136+
// an enumerator, the first expression is a discarded-value expression; if
1137+
// the id-expression names a non-static data member, the first expression
1138+
// shall be a glvalue.
1139+
auto MakeDiscardedValue = [&BaseExpr, IsArrow, this] {
1140+
assert(getLangOpts().CPlusPlus &&
1141+
"Static member / member enumerator outside of C++");
1142+
if (IsArrow)
1143+
return false;
1144+
ExprResult Converted = IgnoredValueConversions(BaseExpr);
1145+
if (Converted.isInvalid())
1146+
return true;
1147+
BaseExpr = Converted.get();
1148+
DiagnoseUnusedExprResult(BaseExpr,
1149+
diag::warn_discarded_class_member_access);
1150+
return false;
1151+
};
1152+
auto MakeGLValue = [&BaseExpr, IsArrow, this] {
1153+
if (IsArrow || !BaseExpr->isPRValue())
1154+
return false;
1155+
ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
1156+
if (Converted.isInvalid())
1157+
return true;
1158+
BaseExpr = Converted.get();
1159+
return false;
1160+
};
1161+
11431162
// Check the use of this member.
11441163
if (DiagnoseUseOfDecl(MemberDecl, MemberLoc))
11451164
return ExprError();
11461165

1147-
if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl))
1166+
if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl)) {
1167+
if (MakeGLValue())
1168+
return ExprError();
11481169
return BuildFieldReferenceExpr(BaseExpr, IsArrow, OpLoc, SS, FD, FoundDecl,
11491170
MemberNameInfo);
1171+
}
11501172

1151-
if (MSPropertyDecl *PD = dyn_cast<MSPropertyDecl>(MemberDecl))
1173+
if (MSPropertyDecl *PD = dyn_cast<MSPropertyDecl>(MemberDecl)) {
1174+
// Properties treated as non-static data members for the purpose of
1175+
// temporary materialization
1176+
if (MakeGLValue())
1177+
return ExprError();
11521178
return BuildMSPropertyRefExpr(*this, BaseExpr, IsArrow, SS, PD,
11531179
MemberNameInfo);
1180+
}
11541181

1155-
if (IndirectFieldDecl *FD = dyn_cast<IndirectFieldDecl>(MemberDecl))
1182+
if (IndirectFieldDecl *FD = dyn_cast<IndirectFieldDecl>(MemberDecl)) {
1183+
if (MakeGLValue())
1184+
return ExprError();
11561185
// We may have found a field within an anonymous union or struct
11571186
// (C++ [class.union]).
11581187
return BuildAnonymousStructUnionMemberReference(SS, MemberLoc, FD,
11591188
FoundDecl, BaseExpr,
11601189
OpLoc);
1190+
}
11611191

1192+
// Static data member
11621193
if (VarDecl *Var = dyn_cast<VarDecl>(MemberDecl)) {
1194+
if (MakeDiscardedValue())
1195+
return ExprError();
11631196
return BuildMemberExpr(BaseExpr, IsArrow, OpLoc,
11641197
SS.getWithLocInContext(Context), TemplateKWLoc, Var,
11651198
FoundDecl, /*HadMultipleCandidates=*/false,
@@ -1174,6 +1207,9 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
11741207
valueKind = VK_PRValue;
11751208
type = Context.BoundMemberTy;
11761209
} else {
1210+
// Static member function
1211+
if (MakeDiscardedValue())
1212+
return ExprError();
11771213
valueKind = VK_LValue;
11781214
type = MemberFn->getType();
11791215
}
@@ -1186,13 +1222,17 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
11861222
assert(!isa<FunctionDecl>(MemberDecl) && "member function not C++ method?");
11871223

11881224
if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(MemberDecl)) {
1225+
if (MakeDiscardedValue())
1226+
return ExprError();
11891227
return BuildMemberExpr(
11901228
BaseExpr, IsArrow, OpLoc, SS.getWithLocInContext(Context),
11911229
TemplateKWLoc, Enum, FoundDecl, /*HadMultipleCandidates=*/false,
11921230
MemberNameInfo, Enum->getType(), VK_PRValue, OK_Ordinary);
11931231
}
11941232

11951233
if (VarTemplateDecl *VarTempl = dyn_cast<VarTemplateDecl>(MemberDecl)) {
1234+
if (MakeDiscardedValue())
1235+
return ExprError();
11961236
if (!TemplateArgs) {
11971237
diagnoseMissingTemplateArguments(
11981238
SS, /*TemplateKeyword=*/TemplateKWLoc.isValid(), VarTempl, MemberLoc);

clang/lib/Sema/SemaOverload.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5922,7 +5922,9 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization(
59225922
DestType = ImplicitParamRecordType;
59235923
FromClassification = From->Classify(Context);
59245924

5925-
// When performing member access on a prvalue, materialize a temporary.
5925+
// CWG2813 [expr.call]p6:
5926+
// If the function is an implicit object member function, the object
5927+
// expression of the class member access shall be a glvalue [...]
59265928
if (From->isPRValue()) {
59275929
From = CreateMaterializeTemporaryExpr(FromRecordType, From,
59285930
Method->getRefQualifier() !=
@@ -6457,11 +6459,6 @@ static Expr *GetExplicitObjectExpr(Sema &S, Expr *Obj,
64576459
VK_LValue, OK_Ordinary, SourceLocation(),
64586460
/*CanOverflow=*/false, FPOptionsOverride());
64596461
}
6460-
if (Obj->Classify(S.getASTContext()).isPRValue()) {
6461-
Obj = S.CreateMaterializeTemporaryExpr(
6462-
ObjType, Obj,
6463-
!Fun->getParamDecl(0)->getType()->isRValueReferenceType());
6464-
}
64656462
return Obj;
64666463
}
64676464

@@ -15709,8 +15706,6 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
1570915706
CurFPFeatureOverrides(), Proto->getNumParams());
1571015707
} else {
1571115708
// Convert the object argument (for a non-static member function call).
15712-
// We only need to do this if there was actually an overload; otherwise
15713-
// it was done at lookup.
1571415709
ExprResult ObjectArg = PerformImplicitObjectArgumentInitialization(
1571515710
MemExpr->getBase(), Qualifier, FoundDecl, Method);
1571615711
if (ObjectArg.isInvalid())

clang/lib/Sema/SemaStmt.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ static bool DiagnoseNoDiscard(Sema &S, const WarnUnusedResultAttr *A,
223223
}
224224

225225
void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
226+
const unsigned OrigDiagID = DiagID;
226227
if (const LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S))
227228
return DiagnoseUnusedExprResult(Label->getSubStmt(), DiagID);
228229

@@ -387,9 +388,16 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
387388
// Do not diagnose use of a comma operator in a SFINAE context because the
388389
// type of the left operand could be used for SFINAE, so technically it is
389390
// *used*.
390-
if (DiagID != diag::warn_unused_comma_left_operand || !isSFINAEContext())
391-
DiagIfReachable(Loc, S ? llvm::ArrayRef(S) : std::nullopt,
392-
PDiag(DiagID) << R1 << R2);
391+
if (DiagID == diag::warn_unused_comma_left_operand && isSFINAEContext())
392+
return;
393+
394+
// Don't diagnose discarded left of dot in static class member access
395+
// because its type is "used" to determine the class to access
396+
if (OrigDiagID == diag::warn_discarded_class_member_access)
397+
return;
398+
399+
DiagIfReachable(Loc, S ? llvm::ArrayRef(S) : std::nullopt,
400+
PDiag(DiagID) << R1 << R2);
393401
}
394402

395403
void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) {

clang/test/AST/ast-dump-for-range-lifetime.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,19 +262,19 @@ void test7() {
262262
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
263263
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
264264
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
265-
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
265+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'A &&'
266266
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
267267
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
268268
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
269269
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
270270
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
271-
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
271+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'A &&'
272272
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
273273
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
274274
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
275275
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
276276
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
277-
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
277+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'A &&'
278278
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
279279
// CHECK-NEXT: | `-CallExpr {{.*}} 'A':'P2718R0::A'
280280
// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'A (*)()' <FunctionToPointerDecay>
@@ -429,19 +429,19 @@ void test13() {
429429
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
430430
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
431431
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
432-
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
432+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'A &&'
433433
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
434434
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
435435
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
436436
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
437437
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
438-
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
438+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'A':'P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'A &&'
439439
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'A':'P2718R0::A' (CXXTemporary {{.*}})
440440
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A'
441441
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .g {{.*}}
442442
// CHECK-NEXT: | `-CXXMemberCallExpr {{.*}} 'A':'P2718R0::A' lvalue
443443
// CHECK-NEXT: | `-MemberExpr {{.*}} '<bound member function type>' .r {{.*}}
444-
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'P2718R0::A' xvalue extended by Var {{.*}} '__range1' 'A &&'
444+
// CHECK-NEXT: | `-MaterializeTemporaryExpr {{.*}} 'P2718R0::A' lvalue extended by Var {{.*}} '__range1' 'A &&'
445445
// CHECK-NEXT: | `-CXXBindTemporaryExpr {{.*}} 'P2718R0::A' (CXXTemporary {{.*}})
446446
// CHECK-NEXT: | `-CallExpr {{.*}} 'P2718R0::A'
447447
// CHECK-NEXT: | `-ImplicitCastExpr {{.*}} 'P2718R0::A (*)()' <FunctionToPointerDecay>

clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify -Wc++20-extensions %s
2-
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify -Wc++17-extensions %s
3-
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify -DEXT -Wc++17-extensions -Wc++20-extensions %s
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify=expected -Wc++20-extensions %s
2+
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify=expected,cxx11-17 -Wc++17-extensions %s
3+
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify=expected,cxx11-17,cxx11 -Wc++17-extensions -Wc++20-extensions %s
44

55
struct [[nodiscard]] S {};
66
S get_s();
@@ -124,21 +124,67 @@ void usage() {
124124
}
125125
}; // namespace p1771
126126

127-
#ifdef EXT
128-
// expected-warning@5 {{use of the 'nodiscard' attribute is a C++17 extension}}
129-
// expected-warning@9 {{use of the 'nodiscard' attribute is a C++17 extension}}
130-
// expected-warning@12 {{use of the 'nodiscard' attribute is a C++17 extension}}
131-
// expected-warning@13 {{use of the 'nodiscard' attribute is a C++17 extension}}
132-
// expected-warning@29 {{use of the 'nodiscard' attribute is a C++17 extension}}
133-
// expected-warning@65 {{use of the 'nodiscard' attribute is a C++20 extension}}
134-
// expected-warning@67 {{use of the 'nodiscard' attribute is a C++20 extension}}
135-
// expected-warning@71 {{use of the 'nodiscard' attribute is a C++20 extension}}
136-
// expected-warning@73 {{use of the 'nodiscard' attribute is a C++20 extension}}
137-
// expected-warning@74 {{use of the 'nodiscard' attribute is a C++20 extension}}
138-
// expected-warning@84 {{use of the 'nodiscard' attribute is a C++20 extension}}
139-
// expected-warning@86 {{use of the 'nodiscard' attribute is a C++17 extension}}
140-
// expected-warning@87 {{use of the 'nodiscard' attribute is a C++20 extension}}
141-
// expected-warning@91 {{use of the 'nodiscard' attribute is a C++17 extension}}
142-
// expected-warning@92 {{use of the 'nodiscard' attribute is a C++20 extension}}
143-
// expected-warning@95 {{use of the 'nodiscard' attribute is a C++20 extension}}
127+
namespace discarded_member_access {
128+
struct X {
129+
union {
130+
int variant_member;
131+
};
132+
struct {
133+
int anonymous_struct_member;
134+
};
135+
int data_member;
136+
static int static_data_member;
137+
enum {
138+
unscoped_enum
139+
};
140+
enum class scoped_enum_t {
141+
scoped_enum
142+
};
143+
using enum scoped_enum_t;
144+
// cxx11-17-warning@-1 {{using enum declaration is a C++20 extension}}
145+
146+
void implicit_object_member_function();
147+
static void static_member_function();
148+
#if __cplusplus >= 202302L
149+
void explicit_object_member_function(this X self);
144150
#endif
151+
};
152+
153+
[[nodiscard]] X get_X();
154+
// cxx11-warning@-1 {{use of the 'nodiscard' attribute is a C++17 extension}}
155+
void f() {
156+
(void) get_X().variant_member;
157+
(void) get_X().anonymous_struct_member;
158+
(void) get_X().data_member;
159+
(void) get_X().static_data_member;
160+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
161+
(void) get_X().unscoped_enum;
162+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
163+
(void) get_X().scoped_enum;
164+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
165+
(void) get_X().implicit_object_member_function();
166+
(void) get_X().static_member_function();
167+
// expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}}
168+
#if __cplusplus >= 202302L
169+
(void) get_X().explicit_object_member_function();
170+
#endif
171+
}
172+
} // namespace discarded_member_access
173+
174+
175+
// cxx11-warning@5 {{use of the 'nodiscard' attribute is a C++17 extension}}
176+
// cxx11-warning@9 {{use of the 'nodiscard' attribute is a C++17 extension}}
177+
// cxx11-warning@12 {{use of the 'nodiscard' attribute is a C++17 extension}}
178+
// cxx11-warning@13 {{use of the 'nodiscard' attribute is a C++17 extension}}
179+
// cxx11-warning@29 {{use of the 'nodiscard' attribute is a C++17 extension}}
180+
// cxx11-warning@65 {{use of the 'nodiscard' attribute is a C++20 extension}}
181+
// cxx11-warning@67 {{use of the 'nodiscard' attribute is a C++20 extension}}
182+
// cxx11-warning@71 {{use of the 'nodiscard' attribute is a C++20 extension}}
183+
// cxx11-warning@73 {{use of the 'nodiscard' attribute is a C++20 extension}}
184+
// cxx11-warning@74 {{use of the 'nodiscard' attribute is a C++20 extension}}
185+
// cxx11-warning@84 {{use of the 'nodiscard' attribute is a C++20 extension}}
186+
// cxx11-warning@86 {{use of the 'nodiscard' attribute is a C++17 extension}}
187+
// cxx11-warning@87 {{use of the 'nodiscard' attribute is a C++20 extension}}
188+
// cxx11-warning@91 {{use of the 'nodiscard' attribute is a C++17 extension}}
189+
// cxx11-warning@92 {{use of the 'nodiscard' attribute is a C++20 extension}}
190+
// cxx11-warning@95 {{use of the 'nodiscard' attribute is a C++20 extension}}

clang/test/CXX/drs/cwg28xx.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,23 @@
66
// RUN: %clang_cc1 -std=c++23 -pedantic-errors -verify=expected,since-cxx20,since-cxx23 %s
77
// RUN: %clang_cc1 -std=c++2c -pedantic-errors -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s
88

9+
namespace cwg2813 { // cwg2813: 19
10+
#if __cplusplus >= 202302L
11+
struct X {
12+
X() = default;
13+
14+
X(const X&) = delete;
15+
X& operator=(const X&) = delete;
16+
17+
void f(this X self) { }
18+
};
19+
20+
void f() {
21+
X{}.f();
22+
}
23+
#endif
24+
}
25+
926
namespace cwg2819 { // cwg2819: 19 tentatively ready 2023-12-01
1027
#if __cpp_constexpr >= 202306L
1128
constexpr void* p = nullptr;

clang/test/CodeGenCXX/cxx2b-deducing-this.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ void test_lambda() {
3131
//CHECK: define dso_local void @{{.*}}test_lambda{{.*}}() #0 {
3232
//CHECK: entry:
3333
//CHECK: %agg.tmp = alloca %class.anon, align 1
34-
//CHECK: %ref.tmp = alloca %class.anon, align 1
3534
//CHECK: %call = call noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"()
3635
//CHECK: ret void
3736
//CHECK: }

clang/www/cxx_dr_status.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16687,7 +16687,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
1668716687
<td><a href="https://cplusplus.github.io/CWG/issues/2813.html">2813</a></td>
1668816688
<td>DR</td>
1668916689
<td>Class member access with prvalues</td>
16690-
<td class="unknown" align="center">Unknown</td>
16690+
<td class="unreleased" align="center">Clang 19</td>
1669116691
</tr>
1669216692
<tr class="open" id="2814">
1669316693
<td><a href="https://cplusplus.github.io/CWG/issues/2814.html">2814</a></td>

0 commit comments

Comments
 (0)