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 <mi...@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@-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@-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@-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@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (void) get_X().unscoped_enum; + // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + (void) get_X().scoped_enum; + // expected-warning@-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@-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@-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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits