[clang-tools-extra] db93ef1 - [Clang] Implement CWG2813: Class member access with prvalues (#120223)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Dec 18 01:44:46 PST 2024
Author: cor3ntin
Date: 2024-12-18T10:44:42+01:00
New Revision: db93ef14aef9c572e02bc842762bc4d0278148f9
URL: https://github.com/llvm/llvm-project/commit/db93ef14aef9c572e02bc842762bc4d0278148f9
DIFF: https://github.com/llvm/llvm-project/commit/db93ef14aef9c572e02bc842762bc4d0278148f9.diff
LOG: [Clang] Implement CWG2813: Class member access with prvalues (#120223)
This is a rebase of #95112 with my own feedback apply as @MitalAshok has
been inactive for a while.
It's fairly important this makes clang 20 as it is a blocker for #107451
---
[CWG2813](https://cplusplus.github.io/CWG/issues/2813.html)
prvalue.member_fn(expression-list) now will not materialize a temporary
for prvalue if member_fn is an explicit object member function, and
prvalue will bind directly to the object parameter.
The E1 in E1.static_member is now a discarded-value expression, so if E1
was a call to a [[nodiscard]] function, there will now be a warning.
This also affects C++98 with [[gnu::warn_unused_result]] functions.
This should not affect C where TemporaryMaterializationConversion is a
no-op.
Closes #100314
Fixes #100341
---------
Co-authored-by: Mital Ashok <mital at mitalashok.co.uk>
Added:
Modified:
clang-tools-extra/clangd/unittests/DumpASTTests.cpp
clang/docs/ReleaseNotes.rst
clang/include/clang/Sema/Sema.h
clang/lib/AST/Expr.cpp
clang/lib/Sema/SemaExprMember.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaStmt.cpp
clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
clang/test/CXX/drs/cwg28xx.cpp
clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
clang/test/SemaCXX/cxx2b-deducing-this.cpp
clang/test/SemaCXX/ms-property.cpp
clang/www/cxx_dr_status.html
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
index 304682118c871d..cb2c17ad4ef0d9 100644
--- a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
@@ -49,7 +49,7 @@ declaration: Function - root
)"},
{R"cpp(
namespace root {
-struct S { static const int x = 0; };
+struct S { static const int x = 0; ~S(); };
int y = S::x + root::S().x;
}
)cpp",
@@ -60,10 +60,12 @@ declaration: Namespace - root
type: Qualified - const
type: Builtin - int
expression: IntegerLiteral - 0
+ declaration: CXXDestructor
+ type: Record - S
+ type: FunctionProto
+ type: Builtin - void
declaration: CXXConstructor
declaration: CXXConstructor
- declaration: CXXConstructor
- declaration: CXXDestructor
declaration: Var - y
type: Builtin - int
expression: ExprWithCleanups
@@ -74,7 +76,7 @@ declaration: Namespace - root
type: Record - S
expression: ImplicitCast - LValueToRValue
expression: Member - x
- expression: MaterializeTemporary - rvalue
+ expression: CXXBindTemporary
expression: CXXTemporaryObject - S
type: Elaborated
specifier: Namespace - root::
@@ -82,6 +84,37 @@ declaration: Namespace - root
)"},
{R"cpp(
namespace root {
+struct S { static const int x = 0; };
+int y = S::x + root::S().x;
+}
+ )cpp",
+ R"(
+declaration: Namespace - root
+ declaration: CXXRecord - S
+ declaration: Var - x
+ type: Qualified - const
+ type: Builtin - int
+ expression: IntegerLiteral - 0
+ declaration: CXXConstructor
+ declaration: CXXConstructor
+ declaration: CXXConstructor
+ declaration: CXXDestructor
+ declaration: Var - y
+ type: Builtin - int
+ expression: BinaryOperator - +
+ expression: ImplicitCast - LValueToRValue
+ expression: DeclRef - x
+ specifier: TypeSpec
+ type: Record - S
+ expression: ImplicitCast - LValueToRValue
+ expression: Member - x
+ expression: CXXTemporaryObject - S
+ type: Elaborated
+ specifier: Namespace - root::
+ type: Record - S
+ )"},
+ {R"cpp(
+namespace root {
template <typename T> int tmpl() {
(void)tmpl<unsigned>();
return T::value;
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 408b2800f9e79c..956b5532b48f65 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -321,6 +321,11 @@ Resolutions to C++ Defect Reports
- Fix name lookup for a dependent base class that is the current instantiation.
(`CWG591: When a dependent base class is the current instantiation <https://cplusplus.github.io/CWG/issues/591.html>`_).
+- Clang now allows calling explicit object member functions directly with prvalues
+ instead of always materializing a temporary, meaning by-value explicit object parameters
+ do not need to move from a temporary.
+ (`CWG2813: Class member access with prvalues <https://cplusplus.github.io/CWG/issues/2813.html>`_).
+
C Language Changes
------------------
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ae07ed8478f2a8..5ee7ea48cc983c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10659,6 +10659,11 @@ class Sema final : public SemaBase {
SourceLocation EndLoc);
void ActOnForEachDeclStmt(DeclGroupPtrTy Decl);
+ /// DiagnoseDiscardedExprMarkedNodiscard - Given an expression that is
+ /// semantically a discarded-value expression, diagnose if any [[nodiscard]]
+ /// value has been discarded.
+ void DiagnoseDiscardedExprMarkedNodiscard(const Expr *E);
+
/// DiagnoseUnusedExprResult - If the statement passed in is an expression
/// whose result is unused, warn.
void DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID);
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 5a6738196d2890..8c8ccdb61dc01c 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -2990,6 +2990,9 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc,
case ExprWithCleanupsClass:
return cast<ExprWithCleanups>(this)->getSubExpr()
->isUnusedResultAWarning(WarnE, Loc, R1, R2, Ctx);
+ case OpaqueValueExprClass:
+ return cast<OpaqueValueExpr>(this)->getSourceExpr()->isUnusedResultAWarning(
+ WarnE, Loc, R1, R2, Ctx);
}
}
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index 85d5dfcb3db6de..bcc1b92ffdec73 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1003,15 +1003,6 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
: !isDependentScopeSpecifier(SS) || computeDeclContext(SS)) &&
"dependent lookup context that isn't the current instantiation?");
- // C++1z [expr.ref]p2:
- // For the first option (dot) the first expression shall be a glvalue [...]
- if (!IsArrow && BaseExpr && BaseExpr->isPRValue()) {
- ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
- if (Converted.isInvalid())
- return ExprError();
- BaseExpr = Converted.get();
- }
-
const DeclarationNameInfo &MemberNameInfo = R.getLookupNameInfo();
DeclarationName MemberName = MemberNameInfo.getName();
SourceLocation MemberLoc = MemberNameInfo.getLoc();
@@ -1128,26 +1119,68 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
BaseExpr = BuildCXXThisExpr(Loc, BaseExprType, /*IsImplicit=*/true);
}
+ // C++17 [expr.ref]p2, per CWG2813:
+ // For the first option (dot), if the id-expression names a static member or
+ // an enumerator, the first expression is a discarded-value expression; if
+ // the id-expression names a non-static data member, the first expression
+ // shall be a glvalue.
+ auto ConvertBaseExprToDiscardedValue = [&] {
+ assert(getLangOpts().CPlusPlus &&
+ "Static member / member enumerator outside of C++");
+ if (IsArrow)
+ return false;
+ ExprResult Converted = IgnoredValueConversions(BaseExpr);
+ if (Converted.isInvalid())
+ return true;
+ BaseExpr = Converted.get();
+ DiagnoseDiscardedExprMarkedNodiscard(BaseExpr);
+ return false;
+ };
+ auto ConvertBaseExprToGLValue = [&] {
+ if (IsArrow || !BaseExpr->isPRValue())
+ return false;
+ ExprResult Converted = TemporaryMaterializationConversion(BaseExpr);
+ if (Converted.isInvalid())
+ return true;
+ BaseExpr = Converted.get();
+ return false;
+ };
+
// Check the use of this member.
if (DiagnoseUseOfDecl(MemberDecl, MemberLoc))
return ExprError();
- if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl))
+ if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl)) {
+ if (ConvertBaseExprToGLValue())
+ return ExprError();
return BuildFieldReferenceExpr(BaseExpr, IsArrow, OpLoc, SS, FD, FoundDecl,
MemberNameInfo);
+ }
- if (MSPropertyDecl *PD = dyn_cast<MSPropertyDecl>(MemberDecl))
+ if (MSPropertyDecl *PD = dyn_cast<MSPropertyDecl>(MemberDecl)) {
+ // No temporaries are materialized for property references yet.
+ // They might be materialized when this is transformed into a member call.
+ // Note that this is slightly
diff erent behaviour from MSVC which doesn't
+ // implement CWG2813 yet: MSVC might materialize an extra temporary if the
+ // getter or setter function is an explicit object member function.
return BuildMSPropertyRefExpr(*this, BaseExpr, IsArrow, SS, PD,
MemberNameInfo);
+ }
- if (IndirectFieldDecl *FD = dyn_cast<IndirectFieldDecl>(MemberDecl))
+ if (IndirectFieldDecl *FD = dyn_cast<IndirectFieldDecl>(MemberDecl)) {
+ if (ConvertBaseExprToGLValue())
+ return ExprError();
// We may have found a field within an anonymous union or struct
// (C++ [class.union]).
return BuildAnonymousStructUnionMemberReference(SS, MemberLoc, FD,
FoundDecl, BaseExpr,
OpLoc);
+ }
+ // Static data member
if (VarDecl *Var = dyn_cast<VarDecl>(MemberDecl)) {
+ if (ConvertBaseExprToDiscardedValue())
+ return ExprError();
return BuildMemberExpr(BaseExpr, IsArrow, OpLoc,
SS.getWithLocInContext(Context), TemplateKWLoc, Var,
FoundDecl, /*HadMultipleCandidates=*/false,
@@ -1161,7 +1194,13 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
if (MemberFn->isInstance()) {
valueKind = VK_PRValue;
type = Context.BoundMemberTy;
+ if (MemberFn->isImplicitObjectMemberFunction() &&
+ ConvertBaseExprToGLValue())
+ return ExprError();
} else {
+ // Static member function
+ if (ConvertBaseExprToDiscardedValue())
+ return ExprError();
valueKind = VK_LValue;
type = MemberFn->getType();
}
@@ -1174,6 +1213,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
assert(!isa<FunctionDecl>(MemberDecl) && "member function not C++ method?");
if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(MemberDecl)) {
+ if (ConvertBaseExprToDiscardedValue())
+ return ExprError();
return BuildMemberExpr(
BaseExpr, IsArrow, OpLoc, SS.getWithLocInContext(Context),
TemplateKWLoc, Enum, FoundDecl, /*HadMultipleCandidates=*/false,
@@ -1181,6 +1222,8 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
}
if (VarTemplateDecl *VarTempl = dyn_cast<VarTemplateDecl>(MemberDecl)) {
+ if (ConvertBaseExprToDiscardedValue())
+ return ExprError();
if (!TemplateArgs) {
diagnoseMissingTemplateArguments(
SS, /*TemplateKeyword=*/TemplateKWLoc.isValid(), VarTempl, MemberLoc);
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 3dabe362802c90..fff49b759c935e 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -5933,7 +5933,9 @@ ExprResult Sema::PerformImplicitObjectArgumentInitialization(
DestType = ImplicitParamRecordType;
FromClassification = From->Classify(Context);
- // When performing member access on a prvalue, materialize a temporary.
+ // CWG2813 [expr.call]p6:
+ // If the function is an implicit object member function, the object
+ // expression of the class member access shall be a glvalue [...]
if (From->isPRValue()) {
From = CreateMaterializeTemporaryExpr(FromRecordType, From,
Method->getRefQualifier() !=
@@ -6464,11 +6466,6 @@ static Expr *GetExplicitObjectExpr(Sema &S, Expr *Obj,
VK_LValue, OK_Ordinary, SourceLocation(),
/*CanOverflow=*/false, FPOptionsOverride());
}
- if (Obj->Classify(S.getASTContext()).isPRValue()) {
- Obj = S.CreateMaterializeTemporaryExpr(
- ObjType, Obj,
- !Fun->getParamDecl(0)->getType()->isRValueReferenceType());
- }
return Obj;
}
@@ -15584,8 +15581,6 @@ ExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE,
CurFPFeatureOverrides(), Proto->getNumParams());
} else {
// Convert the object argument (for a non-static member function call).
- // We only need to do this if there was actually an overload; otherwise
- // it was done at lookup.
ExprResult ObjectArg = PerformImplicitObjectArgumentInitialization(
MemExpr->getBase(), Qualifier, FoundDecl, Method);
if (ObjectArg.isInvalid())
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 0e5c6cd49dccad..d9149f7ee40bbf 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -226,17 +226,18 @@ static bool DiagnoseNoDiscard(Sema &S, const NamedDecl *OffendingDecl,
return S.Diag(Loc, diag::warn_unused_result) << A << true << Msg << R1 << R2;
}
-void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
- if (const LabelStmt *Label = dyn_cast_or_null<LabelStmt>(S))
- return DiagnoseUnusedExprResult(Label->getSubStmt(), DiagID);
+namespace {
- const Expr *E = dyn_cast_or_null<Expr>(S);
- if (!E)
- return;
+// Diagnoses unused expressions that call functions marked [[nodiscard]],
+// [[gnu::warn_unused_result]] and similar.
+// Additionally, a DiagID can be provided to emit a warning in additional
+// contexts (such as for an unused LHS of a comma expression)
+void DiagnoseUnused(Sema &S, const Expr *E, std::optional<unsigned> DiagID) {
+ bool NoDiscardOnly = !DiagID.has_value();
// If we are in an unevaluated expression context, then there can be no unused
// results because the results aren't expected to be used in the first place.
- if (isUnevaluatedContext())
+ if (S.isUnevaluatedContext())
return;
SourceLocation ExprLoc = E->IgnoreParenImpCasts()->getExprLoc();
@@ -245,30 +246,31 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
// expression is a call to a function with the warn_unused_result attribute,
// we warn no matter the location. Because of the order in which the various
// checks need to happen, we factor out the macro-related test here.
- bool ShouldSuppress =
- SourceMgr.isMacroBodyExpansion(ExprLoc) ||
- SourceMgr.isInSystemMacro(ExprLoc);
+ bool ShouldSuppress = S.SourceMgr.isMacroBodyExpansion(ExprLoc) ||
+ S.SourceMgr.isInSystemMacro(ExprLoc);
const Expr *WarnExpr;
SourceLocation Loc;
SourceRange R1, R2;
- if (!E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, Context))
- return;
-
- // If this is a GNU statement expression expanded from a macro, it is probably
- // unused because it is a function-like macro that can be used as either an
- // expression or statement. Don't warn, because it is almost certainly a
- // false positive.
- if (isa<StmtExpr>(E) && Loc.isMacroID())
+ if (!E->isUnusedResultAWarning(WarnExpr, Loc, R1, R2, S.Context))
return;
- // Check if this is the UNREFERENCED_PARAMETER from the Microsoft headers.
- // That macro is frequently used to suppress "unused parameter" warnings,
- // but its implementation makes clang's -Wunused-value fire. Prevent this.
- if (isa<ParenExpr>(E->IgnoreImpCasts()) && Loc.isMacroID()) {
- SourceLocation SpellLoc = Loc;
- if (findMacroSpelling(SpellLoc, "UNREFERENCED_PARAMETER"))
+ if (!NoDiscardOnly) {
+ // If this is a GNU statement expression expanded from a macro, it is
+ // probably unused because it is a function-like macro that can be used as
+ // either an expression or statement. Don't warn, because it is almost
+ // certainly a false positive.
+ if (isa<StmtExpr>(E) && Loc.isMacroID())
return;
+
+ // Check if this is the UNREFERENCED_PARAMETER from the Microsoft headers.
+ // That macro is frequently used to suppress "unused parameter" warnings,
+ // but its implementation makes clang's -Wunused-value fire. Prevent this.
+ if (isa<ParenExpr>(E->IgnoreImpCasts()) && Loc.isMacroID()) {
+ SourceLocation SpellLoc = Loc;
+ if (S.findMacroSpelling(SpellLoc, "UNREFERENCED_PARAMETER"))
+ return;
+ }
}
// Okay, we have an unused result. Depending on what the base expression is,
@@ -279,7 +281,7 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (const CXXBindTemporaryExpr *TempExpr = dyn_cast<CXXBindTemporaryExpr>(E))
E = TempExpr->getSubExpr();
- if (DiagnoseUnusedComparison(*this, E))
+ if (DiagnoseUnusedComparison(S, E))
return;
E = WarnExpr;
@@ -293,8 +295,8 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (E->getType()->isVoidType())
return;
- auto [OffendingDecl, A] = CE->getUnusedResultAttr(Context);
- if (DiagnoseNoDiscard(*this, OffendingDecl,
+ auto [OffendingDecl, A] = CE->getUnusedResultAttr(S.Context);
+ if (DiagnoseNoDiscard(S, OffendingDecl,
cast_or_null<WarnUnusedResultAttr>(A), Loc, R1, R2,
/*isCtor=*/false))
return;
@@ -307,11 +309,11 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (ShouldSuppress)
return;
if (FD->hasAttr<PureAttr>()) {
- Diag(Loc, diag::warn_unused_call) << R1 << R2 << "pure";
+ S.Diag(Loc, diag::warn_unused_call) << R1 << R2 << "pure";
return;
}
if (FD->hasAttr<ConstAttr>()) {
- Diag(Loc, diag::warn_unused_call) << R1 << R2 << "const";
+ S.Diag(Loc, diag::warn_unused_call) << R1 << R2 << "const";
return;
}
}
@@ -323,15 +325,15 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
OffendingDecl = Ctor->getParent();
A = OffendingDecl->getAttr<WarnUnusedResultAttr>();
}
- if (DiagnoseNoDiscard(*this, OffendingDecl, A, Loc, R1, R2,
+ if (DiagnoseNoDiscard(S, OffendingDecl, A, Loc, R1, R2,
/*isCtor=*/true))
return;
}
} else if (const auto *ILE = dyn_cast<InitListExpr>(E)) {
if (const TagDecl *TD = ILE->getType()->getAsTagDecl()) {
- if (DiagnoseNoDiscard(*this, TD, TD->getAttr<WarnUnusedResultAttr>(), Loc,
- R1, R2, /*isCtor=*/false))
+ if (DiagnoseNoDiscard(S, TD, TD->getAttr<WarnUnusedResultAttr>(), Loc, R1,
+ R2, /*isCtor=*/false))
return;
}
} else if (ShouldSuppress)
@@ -339,23 +341,24 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
E = WarnExpr;
if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {
- if (getLangOpts().ObjCAutoRefCount && ME->isDelegateInitCall()) {
- Diag(Loc, diag::err_arc_unused_init_message) << R1;
+ if (S.getLangOpts().ObjCAutoRefCount && ME->isDelegateInitCall()) {
+ S.Diag(Loc, diag::err_arc_unused_init_message) << R1;
return;
}
const ObjCMethodDecl *MD = ME->getMethodDecl();
if (MD) {
- if (DiagnoseNoDiscard(*this, nullptr, MD->getAttr<WarnUnusedResultAttr>(),
- Loc, R1, R2, /*isCtor=*/false))
+ if (DiagnoseNoDiscard(S, nullptr, MD->getAttr<WarnUnusedResultAttr>(),
+ Loc, R1, R2,
+ /*isCtor=*/false))
return;
}
} else if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) {
const Expr *Source = POE->getSyntacticForm();
// Handle the actually selected call of an OpenMP specialized call.
- if (LangOpts.OpenMP && isa<CallExpr>(Source) &&
+ if (S.LangOpts.OpenMP && isa<CallExpr>(Source) &&
POE->getNumSemanticExprs() == 1 &&
isa<CallExpr>(POE->getSemanticExpr(0)))
- return DiagnoseUnusedExprResult(POE->getSemanticExpr(0), DiagID);
+ return DiagnoseUnused(S, POE->getSemanticExpr(0), DiagID);
if (isa<ObjCSubscriptRefExpr>(Source))
DiagID = diag::warn_unused_container_subscript_expr;
else if (isa<ObjCPropertyRefExpr>(Source))
@@ -372,17 +375,21 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
if (!RD->getAttr<WarnUnusedAttr>())
return;
}
+
+ if (NoDiscardOnly)
+ return;
+
// Diagnose "(void*) blah" as a typo for "(void) blah".
- else if (const CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(E)) {
+ if (const CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(E)) {
TypeSourceInfo *TI = CE->getTypeInfoAsWritten();
QualType T = TI->getType();
// We really do want to use the non-canonical type here.
- if (T == Context.VoidPtrTy) {
+ if (T == S.Context.VoidPtrTy) {
PointerTypeLoc TL = TI->getTypeLoc().castAs<PointerTypeLoc>();
- Diag(Loc, diag::warn_unused_voidptr)
- << FixItHint::CreateRemoval(TL.getStarLoc());
+ S.Diag(Loc, diag::warn_unused_voidptr)
+ << FixItHint::CreateRemoval(TL.getStarLoc());
return;
}
}
@@ -391,16 +398,34 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
// isn't an array.
if (E->isGLValue() && E->getType().isVolatileQualified() &&
!E->getType()->isArrayType()) {
- Diag(Loc, diag::warn_unused_volatile) << R1 << R2;
+ S.Diag(Loc, diag::warn_unused_volatile) << R1 << R2;
return;
}
// Do not diagnose use of a comma operator in a SFINAE context because the
// type of the left operand could be used for SFINAE, so technically it is
// *used*.
- if (DiagID != diag::warn_unused_comma_left_operand || !isSFINAEContext())
- DiagIfReachable(Loc, S ? llvm::ArrayRef(S) : llvm::ArrayRef<Stmt *>(),
- PDiag(DiagID) << R1 << R2);
+ if (DiagID == diag::warn_unused_comma_left_operand && S.isSFINAEContext())
+ return;
+
+ S.DiagIfReachable(Loc, llvm::ArrayRef<const Stmt *>(E),
+ S.PDiag(*DiagID) << R1 << R2);
+}
+} // namespace
+
+void Sema::DiagnoseDiscardedExprMarkedNodiscard(const Expr *E) {
+ DiagnoseUnused(*this, E, std::nullopt);
+}
+
+void Sema::DiagnoseUnusedExprResult(const Stmt *S, unsigned DiagID) {
+ if (const LabelStmt *Label = dyn_cast_if_present<LabelStmt>(S))
+ S = Label->getSubStmt();
+
+ const Expr *E = dyn_cast_if_present<Expr>(S);
+ if (!E)
+ return;
+
+ DiagnoseUnused(*this, E, DiagID);
}
void Sema::ActOnStartOfCompoundStmt(bool IsStmtExpr) {
diff --git a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
index da1f8201f55dcc..18f4bd5e9c0fae 100644
--- a/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.attr/dcl.attr.nodiscard/p2.cpp
@@ -1,6 +1,7 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify=expected,cxx11,cxx11-17 -pedantic %s
// RUN: %clang_cc1 -fsyntax-only -std=c++17 -verify=expected,cxx11-17,since-cxx17 -pedantic %s
// RUN: %clang_cc1 -fsyntax-only -std=c++20 -verify=expected,since-cxx17 -pedantic %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++23 -verify=expected,since-cxx17 -pedantic %s
struct [[nodiscard]] S {};
// cxx11-warning at -1 {{use of the 'nodiscard' attribute is a C++17 extension}}
@@ -134,3 +135,50 @@ void usage() {
static_cast<double>(s); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute: Don't throw away as a double}}
}
} // namespace p1771
+
+namespace discarded_member_access {
+struct X {
+ union {
+ int variant_member;
+ };
+ struct { // expected-warning {{anonymous structs are a GNU extension}}
+ int anonymous_struct_member;
+ };
+ int data_member;
+ static int static_data_member;
+ enum {
+ unscoped_enum
+ };
+ enum class scoped_enum_t {
+ scoped_enum
+ };
+ using enum scoped_enum_t;
+ // cxx11-17-warning at -1 {{using enum declaration is a C++20 extension}}
+
+ void implicit_object_member_function();
+ static void static_member_function();
+#if __cplusplus >= 202302L
+ void explicit_object_member_function(this X self);
+#endif
+};
+
+[[nodiscard]] X get_X();
+// cxx11-warning at -1 {{use of the 'nodiscard' attribute is a C++17 extension}}
+void f() {
+ (void) get_X().variant_member;
+ (void) get_X().anonymous_struct_member;
+ (void) get_X().data_member;
+ (void) get_X().static_data_member;
+ // expected-warning at -1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ (void) get_X().unscoped_enum;
+ // expected-warning at -1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ (void) get_X().scoped_enum;
+ // expected-warning at -1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ (void) get_X().implicit_object_member_function();
+ (void) get_X().static_member_function();
+ // expected-warning at -1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+#if __cplusplus >= 202302L
+ (void) get_X().explicit_object_member_function();
+#endif
+}
+} // namespace discarded_member_access
diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp
index 9796607a790ce3..ff625a4a985bcc 100644
--- a/clang/test/CXX/drs/cwg28xx.cpp
+++ b/clang/test/CXX/drs/cwg28xx.cpp
@@ -30,7 +30,25 @@ using U2 = decltype(&main);
#endif
} // namespace cwg2811
-namespace cwg2819 { // cwg2819: 19
+namespace cwg2813 { // cwg2813: 20
+#if __cplusplus >= 202302L
+struct X {
+ X() = default;
+
+ X(const X&) = delete;
+ X& operator=(const X&) = delete;
+
+ void f(this X self) { }
+};
+
+void f() {
+ X{}.f();
+}
+#endif
+} // namespace cwg2813
+
+namespace cwg2819 { // cwg2819: 19 tentatively ready 2023-12-01
+
#if __cpp_constexpr >= 202306L
constexpr void* p = nullptr;
constexpr int* q = static_cast<int*>(p);
diff --git a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
index 1c8835a3986ea0..8a78463d3a4955 100644
--- a/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/CodeGenCXX/cxx2b-deducing-this.cpp
@@ -31,7 +31,6 @@ void test_lambda() {
//CHECK: define dso_local void @{{.*}}test_lambda{{.*}}() #0 {
//CHECK: entry:
//CHECK: %agg.tmp = alloca %class.anon, align 1
-//CHECK: %ref.tmp = alloca %class.anon, align 1
//CHECK: %call = call noundef i32 @"_ZZ11test_lambdavENH3$_0clIS_EEiT_"()
//CHECK: ret void
//CHECK: }
diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
index 520052a89d1840..6f17ce72754560 100644
--- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp
+++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp
@@ -437,6 +437,10 @@ namespace std {
constexpr strong_ordering strong_ordering::equal = {0};
constexpr strong_ordering strong_ordering::greater = {1};
constexpr strong_ordering strong_ordering::less = {-1};
+
+ template<typename T> constexpr __remove_reference_t(T)&& move(T&& t) noexcept {
+ return static_cast<__remove_reference_t(T)&&>(t);
+ }
}
namespace operators_deduction {
@@ -965,6 +969,22 @@ void f();
void a::f(this auto) {} // expected-error {{an explicit object parameter cannot appear in a non-member function}}
}
+namespace GH100341 {
+struct X {
+ X() = default;
+ X(X&&) = default;
+ void operator()(this X);
+};
+
+void fail() {
+ X()();
+ [x = X{}](this auto) {}();
+}
+void pass() {
+ std::move(X())();
+ std::move([x = X{}](this auto) {})();
+}
+} // namespace GH100341
struct R {
void f(this auto &&self, int &&r_value_ref) {} // expected-note {{candidate function template not viable: expects an rvalue for 2nd argument}}
void g(int &&r_value_ref) {
diff --git a/clang/test/SemaCXX/ms-property.cpp b/clang/test/SemaCXX/ms-property.cpp
index 168987b2462233..d5799a8a4d3639 100644
--- a/clang/test/SemaCXX/ms-property.cpp
+++ b/clang/test/SemaCXX/ms-property.cpp
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -ast-print -verify -triple=x86_64-pc-win32 -fms-compatibility %s -o - | FileCheck %s
-// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fms-compatibility -emit-pch -o %t %s
-// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fms-compatibility -include-pch %t -verify %s -ast-print -o - | FileCheck %s
-// expected-no-diagnostics
+// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fms-compatibility -emit-pch -o %t -verify %s
+// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fms-compatibility -include-pch %t %s -ast-print -o - | FileCheck %s
+// RUN: %clang_cc1 -fdeclspec -fsyntax-only -verify %s -std=c++23
#ifndef HEADER
#define HEADER
@@ -85,4 +85,40 @@ int main(int argc, char **argv) {
// CHECK-NEXT: return Test1::GetTest1()->X;
return Test1::GetTest1()->X;
}
+
+struct X {
+ int implicit_object_member_function() { return 0; }
+ static int static_member_function() { return 0; }
+
+ __declspec(property(get=implicit_object_member_function)) int imp;
+ __declspec(property(get=static_member_function)) int st;
+
+#if __cplusplus >= 202302L
+ int explicit_object_member_function(this X self) { return 0; }
+ __declspec(property(get=explicit_object_member_function)) int exp;
+#endif
+};
+
+[[nodiscard]] X get_x();
+void f() {
+ (void) get_x().imp;
+ (void) get_x().st;
+ // expected-warning at -1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+#if __cplusplus >= 202302L
+ (void) get_x().exp;
+#endif
+}
+
+#if __cplusplus >= 202302L
+struct Y {
+ Y() = default;
+ Y(const Y&) = delete;
+ int explicit_object_member_function(this Y) { return 0; }
+ __declspec(property(get = explicit_object_member_function)) int prop;
+};
+void g() {
+ (void) Y().prop;
+}
+#endif
+
#endif // HEADER
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index cdedbcbaa40722..386c57250b7db6 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -16726,7 +16726,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://cplusplus.github.io/CWG/issues/2813.html">2813</a></td>
<td>DRWP</td>
<td>Class member access with prvalues</td>
- <td class="unknown" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 20</td>
</tr>
<tr id="2814">
<td><a href="https://cplusplus.github.io/CWG/issues/2814.html">2814</a></td>
More information about the cfe-commits
mailing list