https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/201544
From b841eedfdb7b2ce61dc7337cb30001a4ce0adfb8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <[email protected]> Date: Thu, 4 Jun 2026 03:39:34 -0700 Subject: [PATCH 1/2] [alpha.webkit.NoDeleteChecker] Allow no-delete default constructors This PR fixes the bug in TrivialFunctionAnalysis that it treats a default trivial constructor without an explicit body / definition as not "trivial". Fixed the bug by allowing the function body to be missing when what we have is a trivial default constructor. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 3 ++ .../Checkers/WebKit/nodelete-annotation.cpp | 42 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index d5ed7fc78148a..90151584a58c6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -667,6 +667,9 @@ class TrivialFunctionAnalysisVisitor if (!Visit(CtorInit->getInit())) return false; } + if (CtorDecl->isTrivial() && CtorDecl->isDefaultConstructor() && + !CtorDecl->hasBody()) + return true; } const Stmt *Body = D->getBody(); if (!Body) diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp index a9c50cfb1f45f..0d78fa91046ed 100644 --- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp +++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp @@ -701,3 +701,45 @@ Ref<RefCountable> [[clang::annotate_type("webkit.nodelete")]] returnTypedefPrval } // namespace returned_prvalue_typedef +namespace create_with_default_constructor { + + struct ObjectWithDefaultConstructorWithoutMemberVariables { + void ref() const; + void deref() const; + + static auto [[clang::annotate_type("webkit.nodelete")]] create() { + return adoptRef(*new ObjectWithDefaultConstructorWithoutMemberVariables()); + } + }; + + struct ObjectWithDefaultConstructorWithPODMemberVariables { + void ref() const; + void deref() const; + + static auto [[clang::annotate_type("webkit.nodelete")]] create() { + return adoptRef(*new ObjectWithDefaultConstructorWithPODMemberVariables()); + } + + private: + int value { 0 }; + RefCountable* ptr { nullptr }; + }; + + struct ObjectWithOpaqueCtor { + ObjectWithOpaqueCtor(); + }; + + struct ObjectWithDefaultConstructorWithOpaqueCtorMemberVariables { + void ref() const; + void deref() const; + + static auto [[clang::annotate_type("webkit.nodelete")]] create() { + return adoptRef(*new ObjectWithDefaultConstructorWithOpaqueCtorMemberVariables()); + // expected-warning@-1{{A function 'create' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object}} + } + + private: + ObjectWithOpaqueCtor obj; + }; + +} // namespace create_with_default_constructor From 3c419fac00a45a160cfab6cdb4ebbde1a7d3c38d Mon Sep 17 00:00:00 2001 From: Balazs Benics <[email protected]> Date: Thu, 4 Jun 2026 14:01:57 +0100 Subject: [PATCH 2/2] [analyzer][WebKit] TrivialFunctionAnalysis: treat trivial implicit / =default special members as trivial IsFunctionTrivial returned false on null FunctionDecl::getBody(). For an implicit or =default special member the synthesized body is materialised only at codegen, so the AST genuinely has no body -- and this conservative rule misclassified guaranteed-trivial special members as ones that could delete. The visible symptom was a false positive on struct Clazzzz { void ref() const; void deref() const; }; Ref<Clazzzz> [[clang::annotate_type("webkit.nodelete")]] create() { return adoptRef(*new Clazzzz()); } `new Clazzzz()` with parens is value-initialization, which emits a CXXConstructExpr for Clazzzz's implicit default ctor; VisitCXXConstructExpr then dispatched to IsFunctionTrivial(getConstructor()), which fell into the null-body branch. (`new Clazzzz` without parens does not emit the CXXConstructExpr for a class with no user-provided default ctor, which is why most existing tests did not hit this.) The same path could affect any implicit/=default ctor or dtor of a class that satisfies the standard's trivial-special-member rules. Fix: in IsFunctionTrivial, before the null-body check, recognise non-user-provided special members and short-circuit to true when the parent class reports the corresponding member trivial via CXXRecordDecl::hasTrivial{Default,Copy,Move}Constructor() / hasTrivialDestructor(). By the C++ standard these run no user code, so they cannot delete -- the fast-path cannot introduce false negatives. Add a minimised regression test (Plain { int x; } + new Plain()) alongside the existing Clazzzz/create() case so the implicit-trivial-ctor path is covered without smart-pointer infrastructure. Assisted-By: claude --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 31 ++++++++++++++++--- .../Checkers/WebKit/nodelete-annotation.cpp | 24 ++++++++++++++ 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 90151584a58c6..cf165796c9695 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -652,23 +652,44 @@ class TrivialFunctionAnalysisVisitor bool IsFunctionTrivial(const Decl *D) { const Stmt **SavedOffendingStmt = std::exchange(OffendingStmt, nullptr); auto Result = WithCachedResult(D, [&]() { - if (auto *FnDecl = dyn_cast<FunctionDecl>(D)) { + auto *FnDecl = dyn_cast<FunctionDecl>(D); + auto *MethodDecl = dyn_cast<CXXMethodDecl>(D); + auto *CtorDecl = dyn_cast<CXXConstructorDecl>(D); + auto *DtorDecl = dyn_cast<CXXDestructorDecl>(D); + + if (FnDecl) { if (isNoDeleteFunction(FnDecl)) return true; - if (auto *MD = dyn_cast<CXXMethodDecl>(D); MD && MD->isVirtual()) + if (MethodDecl && MethodDecl->isVirtual()) return false; for (auto *Param : FnDecl->parameters()) { if (!HasTrivialDestructor(Param)) return false; } } - if (auto *CtorDecl = dyn_cast<CXXConstructorDecl>(D)) { + if (CtorDecl) { for (auto *CtorInit : CtorDecl->inits()) { if (!Visit(CtorInit->getInit())) return false; } - if (CtorDecl->isTrivial() && CtorDecl->isDefaultConstructor() && - !CtorDecl->hasBody()) + } + // An implicit or =default special member runs no user code when it is + // trivial in the C++ standard sense, so it cannot delete. Such a + // member's synthesized body is typically absent from the AST until + // codegen materialises it, which the generic null-body check below + // would otherwise conservatively classify as non-trivial. + if (MethodDecl && !MethodDecl->isUserProvided()) { + if (CtorDecl) { + const CXXRecordDecl *RD = CtorDecl->getParent(); + if ((CtorDecl->isDefaultConstructor() && + RD->hasTrivialDefaultConstructor()) || + (CtorDecl->isCopyConstructor() && + RD->hasTrivialCopyConstructor()) || + (CtorDecl->isMoveConstructor() && + RD->hasTrivialMoveConstructor())) + return true; + } + if (DtorDecl && DtorDecl->getParent()->hasTrivialDestructor()) return true; } const Stmt *Body = D->getBody(); diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp index 0d78fa91046ed..17b8bd83aca5d 100644 --- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp +++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp @@ -743,3 +743,27 @@ namespace create_with_default_constructor { }; } // namespace create_with_default_constructor + +struct Clazzzz { + void ref() const; + void deref() const; +}; + +Ref<Clazzzz> [[clang::annotate_type("webkit.nodelete")]] create() { + return adoptRef(*new Clazzzz()); +} + +namespace trivial_implicit_ctor_in_new_expr { + +// 'new T()' with parens emits a CXXConstructExpr for T's implicit default +// ctor. That ctor has no body in the AST (the synthesized body is materialised +// only at codegen), but it is trivial by the C++ standard and runs no user +// code, so it cannot delete. Verify the fast-path treats it as trivial. +struct Plain { int x; }; + +void [[clang::annotate_type("webkit.nodelete")]] valueInitNew() { + Plain* p = new Plain(); + (void)p; +} + +} // namespace trivial_implicit_ctor_in_new_expr _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
