Skip to content

Commit 9f2e636

Browse files
Shubham Shuklavgvassilev
authored andcommitted
Register a cloned lambda body's nested declarations before remapping.
1 parent 7514327 commit 9f2e636

2 files changed

Lines changed: 40 additions & 14 deletions

File tree

lib/Differentiator/VisitorBase.cpp

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,30 @@ namespace clad {
10131013
return newPVD;
10141014
}
10151015

1016+
#if CLANG_VERSION_MAJOR >= 17
1017+
/// Register every (original, clone) VarDecl pair, at any depth.
1018+
static void
1019+
registerClonedDecls(const Stmt* Orig, Stmt* Cloned,
1020+
std::unordered_map<const VarDecl*, VarDecl*>& Repls) {
1021+
if (!Orig || !Cloned)
1022+
return;
1023+
if (const auto* DS = dyn_cast<DeclStmt>(Orig))
1024+
if (auto* ClonedDS = dyn_cast<DeclStmt>(Cloned)) {
1025+
auto O = DS->decl_begin();
1026+
auto C = ClonedDS->decl_begin();
1027+
for (; O != DS->decl_end() && C != ClonedDS->decl_end(); ++O, ++C)
1028+
if (const auto* OVD = dyn_cast<VarDecl>(*O))
1029+
if (auto* CVD = dyn_cast<VarDecl>(*C))
1030+
if (OVD != CVD)
1031+
Repls[OVD] = CVD;
1032+
}
1033+
auto O = Orig->child_begin();
1034+
auto C = Cloned->child_begin();
1035+
for (; O != Orig->child_end() && C != Cloned->child_end(); ++O, ++C)
1036+
registerClonedDecls(*O, *C, Repls);
1037+
}
1038+
#endif // CLANG_VERSION_MAJOR >= 17
1039+
10161040
Expr* VisitorBase::buildClonedLambda(const LambdaExpr* LE) {
10171041
// A primal copy needs a *fresh* closure type; a plain StmtClone reuses the
10181042
// original closure, so two clones share the operator() body -- both a
@@ -1161,23 +1185,11 @@ namespace clad {
11611185
// lambda's call operator rather than m_DiffReq.Function (the outer
11621186
// function being differentiated), which would reject the lambda locals.
11631187
Stmt* clonedS = CloneNode(S);
1188+
// Register before remapping, so references in this statement update too.
1189+
registerClonedDecls(S, clonedS, m_DeclReplacements);
11641190
utils::ReferencesUpdater up(m_Sema, getCurrentScope(), CallOp,
11651191
m_DeclReplacements);
11661192
up.TraverseStmt(clonedS);
1167-
// Cloning a DeclStmt produces a fresh VarDecl, but StmtClone records that
1168-
// only in its own decl mapping. Register it here too, or a later
1169-
// statement's clone keeps referring to the original lambda's variable --
1170-
// a reference into the user's AST that outlives differentiation.
1171-
if (const auto* DS = dyn_cast<DeclStmt>(S))
1172-
if (auto* ClonedDS = dyn_cast<DeclStmt>(clonedS)) {
1173-
auto O = DS->decl_begin();
1174-
auto C = ClonedDS->decl_begin();
1175-
for (; O != DS->decl_end() && C != ClonedDS->decl_end(); ++O, ++C)
1176-
if (const auto* OVD = dyn_cast<VarDecl>(*O))
1177-
if (auto* CVD = dyn_cast<VarDecl>(*C))
1178-
if (OVD != CVD)
1179-
m_DeclReplacements[OVD] = CVD;
1180-
}
11811193
addToCurrentBlock(clonedS);
11821194
}
11831195
CompoundStmt* ClonedBody = endBlock();

test/ForwardMode/Lambdas.C

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ double fn4(double x) {
4242
return _f(x);
4343
}
4444

45+
double fn5(double x) {
46+
auto _f = [](double _x) {
47+
double s = 0;
48+
for (int k = 0; k < 3; ++k)
49+
s += _x * _x;
50+
return s;
51+
};
52+
return _f(x);
53+
}
54+
4555
int main() {
4656
auto fn0_dx = clad::differentiate(fn0, 0);
4757
printf("Result is = %.2f\n", fn0_dx.execute(7)); // CHECK-EXEC: Result is = 14.00
@@ -62,4 +72,8 @@ int main() {
6272
auto fn4_dx = clad::differentiate(fn4, 0);
6373
printf("Result is = %.2f\n", fn4_dx.execute(7)); // CHECK-EXEC: Result is = 28.00
6474
printf("Result is = %.2f\n", fn4_dx.execute(-1)); // CHECK-EXEC: Result is = -4.00
75+
76+
auto fn5_dx = clad::differentiate(fn5, 0);
77+
printf("Result is = %.2f\n", fn5_dx.execute(7)); // CHECK-EXEC: Result is = 42.00
78+
printf("Result is = %.2f\n", fn5_dx.execute(-1)); // CHECK-EXEC: Result is = -6.00
6579
}

0 commit comments

Comments
 (0)