Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 87 additions & 30 deletions lib/Differentiator/DerivativeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,45 @@
#include <cstddef>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>

using namespace clang;

namespace {
// clad's AST-node-reuse diagnostics belong in the "clad-plugin-node-reuse"
// warning group -- a subgroup of clad's "clad-plugin" group -- when the host
// clang supports plugin diagnostic groups, so users can control them with
// -Wno-clad-plugin-node-reuse (or -Wno-clad-plugin, or -Wno-plugin) like any
// other warning. Detect the three-argument getCustomDiagID(Level, format,
// group) overload; older clang has only the two-argument form and the
// diagnostics stay ungrouped.
constexpr llvm::StringLiteral CladNodeReuseGroup = "clad-plugin-node-reuse";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: no header providing "llvm::StringLiteral" is directly included [misc-include-cleaner]

lib/Differentiator/DerivativeBuilder.cpp:51:

- #include <memory>
+ #include <llvm/ADT/StringRef.h>
+ #include <memory>


template <typename T, typename = void>
struct HasPluginDiagGroups : std::false_type {};
template <typename T>
struct HasPluginDiagGroups<
T, std::void_t<decltype(std::declval<T&>().getCustomDiagID(
clang::DiagnosticsEngine::Warning, std::declval<const char (&)[2]>(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

           clang::DiagnosticsEngine::Warning, std::declval<const char (&)[2]>(),
                                                                 ^

std::declval<llvm::StringRef>()))>> : std::true_type {};

// Return the ID of a clad node-reuse warning, placing it in the
// "clad-plugin-node-reuse" group where clang supports it. \p Message's size N
// is a template parameter so the grouped call is type-dependent; `if constexpr`
// then discards it (rather than requiring it to compile) on a clang without the
// group overload.
template <unsigned N>
unsigned getIntegrityDiagID(clang::DiagnosticsEngine& Diags,
const char (&Message)[N]) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays]

                            const char (&Message)[N]) {
                                  ^

if constexpr (HasPluginDiagGroups<clang::DiagnosticsEngine>::value)
return Diags.getCustomDiagID(clang::DiagnosticsEngine::Warning, Message,
CladNodeReuseGroup);
else
return Diags.getCustomDiagID(clang::DiagnosticsEngine::Warning, Message);
}
} // namespace

namespace clad {

DerivativeBuilder::DerivativeBuilder(clang::Sema& S, plugin::CladPlugin& P,
Expand Down Expand Up @@ -654,36 +690,57 @@
// derivatives legitimately share and this check is unreachable.
if (auto* FD = dyn_cast_or_null<clang::FunctionDecl>(result.derivative))
if (clang::Stmt* Body = FD->getBody()) {
const clang::Stmt* Shared = findSharedNode(Body);
// Debug asserts builds abort here; release builds keep the diagnostic
// so a sharing regression is not silently shipped.
assert(!Shared && "clad generated a derivative with a shared AST node");
if (Shared)
diag(DiagnosticsEngine::Warning, FD->getLocation(),
"clad internally reused a '%0' AST node while differentiating "
"%1; this is a clad bug -- please report it at "
"https://github.com/vgvassilev/clad")
<< Shared->getStmtClassName() << FD;

// A derivative must also not splice a node owned by its primal. The
// original function's AST outlives differentiation, so a shared node
// exposes the user's own code to any later in-place edit of the
// derivative -- the same corruption risk findSharedNode guards against,
// across the primal/derivative boundary it cannot see. Enforce it too.
if (const clang::FunctionDecl* PrimalFD = request.Function)
if (const clang::Stmt* PrimalBody = PrimalFD->getBody()) {
const clang::Stmt* FromPrimal =
findPrimalSharedNode(Body, PrimalBody);
assert(!FromPrimal &&
"clad spliced a primal AST node into a derivative");
if (FromPrimal)
diag(DiagnosticsEngine::Warning, FD->getLocation(),
"clad reused a '%0' AST node from the original function "
"while "
"differentiating %1; this is a clad bug -- please report it "
"at https://github.com/vgvassilev/clad")
<< FromPrimal->getStmtClassName() << FD;
}
clang::DiagnosticsEngine& Diags = m_Sema.getDiagnostics();
// Both checks report through the "clad-plugin-node-reuse" warning group
// (where the host clang supports it), so -Wno-clad-plugin-node-reuse
// (or -Wno-clad-plugin, or -Wno-plugin) silences them.
unsigned SharedDiagID = getIntegrityDiagID(
Diags,
"clad internally reused a '%0' AST node while differentiating %1; "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: argument comment missing for literal argument 'Message' [bugprone-argument-comment]

Suggested change
"clad internally reused a '%0' AST node while differentiating %1; "
/*Message=*/"clad internally reused a '%0' AST node while differentiating %1; "

"this is a clad bug -- please report it at "
"https://github.com/vgvassilev/clad");
unsigned PrimalDiagID = getIntegrityDiagID(
Diags,
"clad reused a '%0' AST node from the original function while "

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warning: argument comment missing for literal argument 'Message' [bugprone-argument-comment]

Suggested change
"clad reused a '%0' AST node from the original function while "
/*Message=*/"clad reused a '%0' AST node from the original function while "

"differentiating %1; this is a clad bug -- please report it at "
"https://github.com/vgvassilev/clad");

// The checks below walk the derivative (and its primal) once each. A
// debug build always runs them so the asserts fire; a release build
// skips the walks entirely when the user has silenced the group (e.g.
// -Wno-clad-plugin, or -w), since the diagnostic is then all they buy.
bool RunChecks = true;
#ifdef NDEBUG
RunChecks = !Diags.isIgnored(SharedDiagID, clang::SourceLocation());
#endif
if (RunChecks) {
// A generated derivative must be a proper tree: no node is the child
// (Stmt::children()) of two parents, because a later in-place edit of
// a shared node leaks into its other users (and a shared aggregate
// initializer breaks CodeGen).
const clang::Stmt* Shared = findSharedNode(Body);
assert(!Shared &&
"clad generated a derivative with a shared AST node");
if (Shared)
m_Sema.Diag(FD->getLocation(), SharedDiagID)
<< Shared->getStmtClassName() << FD;

Check warning on line 726 in lib/Differentiator/DerivativeBuilder.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Differentiator/DerivativeBuilder.cpp#L725-L726

Added lines #L725 - L726 were not covered by tests

// A derivative must also not splice a node owned by its primal. The
// original function's AST outlives differentiation, so a shared node
// exposes the user's own code to any later in-place edit of the
// derivative -- the same corruption risk, across the
// primal/derivative boundary findSharedNode cannot see.
if (const clang::FunctionDecl* PrimalFD = request.Function)
if (const clang::Stmt* PrimalBody = PrimalFD->getBody()) {
const clang::Stmt* FromPrimal =
findPrimalSharedNode(Body, PrimalBody);
assert(!FromPrimal &&
"clad spliced a primal AST node into a derivative");
if (FromPrimal)
m_Sema.Diag(FD->getLocation(), PrimalDiagID)
<< FromPrimal->getStmtClassName() << FD;

Check warning on line 741 in lib/Differentiator/DerivativeBuilder.cpp

View check run for this annotation

Codecov / codecov/patch

lib/Differentiator/DerivativeBuilder.cpp#L740-L741

Added lines #L740 - L741 were not covered by tests
}
}
}
#endif

Expand Down
Loading