diff --git a/include/clad/Differentiator/BaseForwardModeVisitor.h b/include/clad/Differentiator/BaseForwardModeVisitor.h index 311fc5769..507698135 100644 --- a/include/clad/Differentiator/BaseForwardModeVisitor.h +++ b/include/clad/Differentiator/BaseForwardModeVisitor.h @@ -3,6 +3,7 @@ #include "Compatibility.h" #include "VisitorBase.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/Sema/Sema.h" @@ -140,6 +141,16 @@ class BaseForwardModeVisitor /// \return active switch case label after processing `stmt` clang::SwitchCase* DeriveSwitchStmtBodyHelper(const clang::Stmt* stmt, clang::SwitchCase* activeSC); + + /// Tries to build custom derivative constructor pushforward call for the + /// given CXXConstructExpr. + /// + /// \return A call expression if a suitable custom derivative is found; + /// Otherwise returns nullptr. + clang::Expr* BuildCustomDerivativeConstructorPFCall( + const clang::CXXConstructExpr* CE, + llvm::SmallVectorImpl& clonedArgs, + llvm::SmallVectorImpl& derivedArgs); }; } // end namespace clad diff --git a/include/clad/Differentiator/BuiltinDerivatives.h b/include/clad/Differentiator/BuiltinDerivatives.h index 0482043b0..85937f72b 100644 --- a/include/clad/Differentiator/BuiltinDerivatives.h +++ b/include/clad/Differentiator/BuiltinDerivatives.h @@ -21,6 +21,26 @@ template struct ValueAndPushforward { T value; U pushforward; }; + +/// It is used to identify constructor custom pushforwards. For +/// constructor custom pushforward functions, we cannot use the same +/// strategy which we use for custom pushforward for member +/// functions. Member functions custom pushforward have the following +/// signature: +/// +/// mem_fn_pushforward(ClassName *c, ..., ClassName *d_c, ...) +/// +/// We use the first argument 'ClassName *c' to determine the class of member +/// function for which the pushforward is defined. +/// +/// In the case of constructor pushforward, there are no objects of the class +/// type passed to the constructor. Therefore, we cannot simply use arguments +/// to determine the class. To solve this, 'ConstructorPushforwardTag' is +/// used. A custom_derivative pushforward for constructor is required to have +/// 'ConstructorPushforwardTag' as the first argument, where 'T' is the +/// class for which constructor pushforward is defined. +template class ConstructorPushforwardTag {}; + namespace custom_derivatives { #ifdef __CUDACC__ template diff --git a/include/clad/Differentiator/STLBuiltins.h b/include/clad/Differentiator/STLBuiltins.h index 39e786aba..12a8048f7 100644 --- a/include/clad/Differentiator/STLBuiltins.h +++ b/include/clad/Differentiator/STLBuiltins.h @@ -1,8 +1,9 @@ #ifndef CLAD_STL_BUILTINS_H #define CLAD_STL_BUILTINS_H +#include +#include #include -#include "clad/Differentiator/BuiltinDerivatives.h" namespace clad { namespace custom_derivatives { @@ -43,6 +44,90 @@ end_pushforward(const ::std::initializer_list* il, const ::std::initializer_list* d_il) { return {il->end(), d_il->end()}; } + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward( + ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, + const typename ::std::vector::allocator_type& alloc, + typename ::std::vector::size_type d_n, + const typename ::std::vector::allocator_type& d_alloc) { + ::std::vector v(n, alloc); + ::std::vector d_v(n, 0, alloc); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward( + ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, T val, + const typename ::std::vector::allocator_type& alloc, + typename ::std::vector::size_type d_n, T d_val, + const typename ::std::vector::allocator_type& d_alloc) { + ::std::vector v(n, val, alloc); + ::std::vector d_v(n, d_val, alloc); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward( + ConstructorPushforwardTag<::std::vector>, + ::std::initializer_list list, + const typename ::std::vector::allocator_type& alloc, + ::std::initializer_list dlist, + const typename ::std::vector::allocator_type& dalloc) { + ::std::vector v(list, alloc); + ::std::vector d_v(dlist, dalloc); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward(ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, + typename ::std::vector::size_type d_n) { + ::std::vector v(n); + ::std::vector d_v(n, 0); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward(ConstructorPushforwardTag<::std::vector>, + typename ::std::vector::size_type n, T val, + typename ::std::vector::size_type d_n, T d_val) { + ::std::vector v(n, val); + ::std::vector d_v(n, d_val); + return {v, d_v}; +} + +template +clad::ValueAndPushforward<::std::vector, ::std::vector> +constructor_pushforward(ConstructorPushforwardTag<::std::vector>, + ::std::initializer_list list, + ::std::initializer_list dlist) { + ::std::vector v(list); + ::std::vector d_v(dlist); + return {v, d_v}; +} + +template +ValueAndPushforward +operator_subscript_pushforward(::std::vector* v, unsigned idx, + ::std::vector* d_v, unsigned d_idx) { + return {(*v)[idx], (*d_v)[idx]}; +} + +template +ValueAndPushforward +operator_subscript_pushforward(const ::std::vector* v, unsigned idx, + const ::std::vector* d_v, unsigned d_idx) { + return {(*v)[idx], (*d_v)[idx]}; +} + } // namespace class_functions } // namespace custom_derivatives } // namespace clad diff --git a/include/clad/Differentiator/VisitorBase.h b/include/clad/Differentiator/VisitorBase.h index 67c295904..d61031f19 100644 --- a/include/clad/Differentiator/VisitorBase.h +++ b/include/clad/Differentiator/VisitorBase.h @@ -18,7 +18,6 @@ namespace clad { #include "clang/AST/StmtVisitor.h" #include "clang/Sema/ParsedAttr.h" #include "clang/Sema/Sema.h" - #include #include #include @@ -600,6 +599,13 @@ namespace clad { bool isDerived); clang::QualType DetermineCladArrayValueType(clang::QualType T); + + /// Returns clad::Identify template declaration. + clang::TemplateDecl* GetCladConstructorPushforwardTag(); + + /// Returns type clad::Identify + clang::QualType GetCladConstructorPushforwardTagOfType(clang::QualType T); + public: /// Rebuild a sequence of nested namespaces ending with DC. clang::NamespaceDecl* RebuildEnclosingNamespaces(clang::DeclContext* DC); @@ -644,6 +650,9 @@ namespace clad { void ComputeEffectiveDOperands(StmtDiff& LDiff, StmtDiff& RDiff, clang::Expr*& derivedL, clang::Expr*& derivedR); + + private: + clang::TemplateDecl* m_CladConstructorPushforwardTag = nullptr; }; } // end namespace clad diff --git a/lib/Differentiator/BaseForwardModeVisitor.cpp b/lib/Differentiator/BaseForwardModeVisitor.cpp index 2e26444e4..4dd6fd8f1 100644 --- a/lib/Differentiator/BaseForwardModeVisitor.cpp +++ b/lib/Differentiator/BaseForwardModeVisitor.cpp @@ -1985,6 +1985,26 @@ BaseForwardModeVisitor::VisitCXXConstructExpr(const CXXConstructExpr* CE) { clonedArgs.push_back(argDiff.getExpr()); derivedArgs.push_back(argDiff.getExpr_dx()); } + + Expr* pushforwardCall = + BuildCustomDerivativeConstructorPFCall(CE, clonedArgs, derivedArgs); + if (pushforwardCall) { + auto valueAndPushforwardE = StoreAndRef(pushforwardCall); + Expr* valueE = utils::BuildMemberExpr(m_Sema, getCurrentScope(), + valueAndPushforwardE, "value"); + Expr* pushforwardE = utils::BuildMemberExpr( + m_Sema, getCurrentScope(), valueAndPushforwardE, "pushforward"); + return StmtDiff(valueE, pushforwardE); + } + + // Custom derivative not found. Create simple constructor calls based on the + // given arguments. For example, if the primal constructor call is + // 'C(a, b, c)' then we use the constructor call 'C(d_a, d_b, d_c)' for the + // derivative. + // FIXME: This is incorrect. It only works for very simple types such as + // std::complex. We should ideally treat a constructor like a function and + // thus differentiate its body, create a pushforward and use the pushforward + // in the derivative code instead of the original constructor. Expr* clonedArgsE = nullptr; Expr* derivedArgsE = nullptr; // FIXME: Currently if the original initialisation expression is `{a, 1, @@ -1996,17 +2016,14 @@ BaseForwardModeVisitor::VisitCXXConstructExpr(const CXXConstructExpr* CE) { if (CE->isListInitialization()) { clonedArgsE = m_Sema.ActOnInitList(noLoc, clonedArgs, noLoc).get(); derivedArgsE = m_Sema.ActOnInitList(noLoc, derivedArgs, noLoc).get(); + } else if (CE->getNumArgs() == 0) { + // ParenList is empty -- default initialisation. + // Passing empty parenList here will silently cause 'most vexing + // parse' issue. + return StmtDiff(); } else { - if (CE->getNumArgs() == 0) { - // ParenList is empty -- default initialisation. - // Passing empty parenList here will silently cause 'most vexing - // parse' issue. - return StmtDiff(); - } else { - clonedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, clonedArgs).get(); - derivedArgsE = - m_Sema.ActOnParenListExpr(noLoc, noLoc, derivedArgs).get(); - } + clonedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, clonedArgs).get(); + derivedArgsE = m_Sema.ActOnParenListExpr(noLoc, noLoc, derivedArgs).get(); } } else { clonedArgsE = clonedArgs[0]; @@ -2045,6 +2062,7 @@ StmtDiff BaseForwardModeVisitor::VisitCXXTemporaryObjectExpr( clonedArgs.push_back(argDiff.getExpr()); derivedArgs.push_back(argDiff.getExpr_dx()); } + Expr* clonedTOE = m_Sema .ActOnCXXTypeConstructExpr(OpaquePtr::make(TOE->getType()), @@ -2183,4 +2201,33 @@ StmtDiff BaseForwardModeVisitor::VisitCXXStdInitializerListExpr( const clang::CXXStdInitializerListExpr* ILE) { return Visit(ILE->getSubExpr()); } + +clang::Expr* BaseForwardModeVisitor::BuildCustomDerivativeConstructorPFCall( + const clang::CXXConstructExpr* CE, + llvm::SmallVectorImpl& clonedArgs, + llvm::SmallVectorImpl& derivedArgs) { + llvm::SmallVector customPushforwardArgs; + QualType constructorPushforwardTagT = GetCladConstructorPushforwardTagOfType( + CE->getType().withoutLocalFastQualifiers()); + // Builds clad::ConstructorPushforwardTag declaration + Expr* constructorPushforwardTagArg = + m_Sema + .BuildCXXTypeConstructExpr( + m_Context.getTrivialTypeSourceInfo(constructorPushforwardTagT, + utils::GetValidSLoc(m_Sema)), + noLoc, MultiExprArg{}, noLoc, /*ListInitialization=*/false) + .get(); + customPushforwardArgs.push_back(constructorPushforwardTagArg); + customPushforwardArgs.append(clonedArgs.begin(), clonedArgs.end()); + customPushforwardArgs.append(derivedArgs.begin(), derivedArgs.end()); + std::string customPushforwardName = + clad::utils::ComputeEffectiveFnName(CE->getConstructor()) + + GetPushForwardFunctionSuffix(); + // FIXME: We should not use const_cast to get the decl context here. + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + Expr* pushforwardCall = m_Builder.BuildCallToCustomDerivativeOrNumericalDiff( + customPushforwardName, customPushforwardArgs, getCurrentScope(), + const_cast(CE->getConstructor()->getDeclContext())); + return pushforwardCall; +} } // end namespace clad diff --git a/lib/Differentiator/CladUtils.cpp b/lib/Differentiator/CladUtils.cpp index 73fd1c5e8..96b35d6aa 100644 --- a/lib/Differentiator/CladUtils.cpp +++ b/lib/Differentiator/CladUtils.cpp @@ -10,6 +10,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Sema/Lookup.h" #include "llvm/ADT/SmallVector.h" +#include #include "clad/Differentiator/Compatibility.h" using namespace clang; @@ -98,6 +99,8 @@ namespace clad { case OverloadedOperatorKind::OO_Subscript: return "operator_subscript"; default: + if (isa(FD)) + return "constructor"; return FD->getNameAsString(); } } diff --git a/lib/Differentiator/VisitorBase.cpp b/lib/Differentiator/VisitorBase.cpp index 780184274..eb462a7fb 100644 --- a/lib/Differentiator/VisitorBase.cpp +++ b/lib/Differentiator/VisitorBase.cpp @@ -811,4 +811,16 @@ namespace clad { return m_Sema.ActOnCallExpr(getCurrentScope(), pushDRE, noLoc, args, noLoc) .get(); } + + clang::TemplateDecl* VisitorBase::GetCladConstructorPushforwardTag() { + if (!m_CladConstructorPushforwardTag) + m_CladConstructorPushforwardTag = + LookupTemplateDeclInCladNamespace("ConstructorPushforwardTag"); + return m_CladConstructorPushforwardTag; + } + + clang::QualType + VisitorBase::GetCladConstructorPushforwardTagOfType(clang::QualType T) { + return InstantiateTemplate(GetCladConstructorPushforwardTag(), {T}); + } } // end namespace clad diff --git a/test/ForwardMode/STLCustomDerivatives.C b/test/ForwardMode/STLCustomDerivatives.C new file mode 100644 index 000000000..dfaf5fd0f --- /dev/null +++ b/test/ForwardMode/STLCustomDerivatives.C @@ -0,0 +1,129 @@ +// RUN: %cladclang %s -I%S/../../include -oUserDefinedTypes.out | %filecheck %s +// RUN: ./UserDefinedTypes.out | %filecheck_exec %s + +// CHECK-NOT: {{.*error|warning|note:.*}} + +#include "clad/Differentiator/Differentiator.h" +#include "clad/Differentiator/STLBuiltins.h" + +#include +#include + +#include "../TestUtils.h" +#include "../PrintOverloads.h" + +double fnVec1(double u, double v) { + std::vector V1(2); + std::vector V2(4); + V1[0] = u; + V1[1] = v; + V2[0] = v; + V2[1] = u; + double res = V1[0] * V1[1] + V2[0] * V2[1]; + return res; +} + +// CHECK: double fnVec1_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, {{.*}}0{{.*}}); +// CHECK-NEXT: std::vector _d_V1(_t0.pushforward); +// CHECK-NEXT: std::vector V1(_t0.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t1 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 4, {{.*}}0{{.*}}); +// CHECK-NEXT: std::vector _d_V2(_t1.pushforward); +// CHECK-NEXT: std::vector V2(_t1.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: _t2.pushforward = _d_u; +// CHECK-NEXT: _t2.value = u; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t3 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 1, &_d_V1, 0); +// CHECK-NEXT: _t3.pushforward = _d_v; +// CHECK-NEXT: _t3.value = v; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t4 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 0, &_d_V2, 0); +// CHECK-NEXT: _t4.pushforward = _d_v; +// CHECK-NEXT: _t4.value = v; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t5 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: _t5.pushforward = _d_u; +// CHECK-NEXT: _t5.value = u; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t6 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t7 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 1, &_d_V1, 0); +// CHECK-NEXT: double &_t8 = _t6.value; +// CHECK-NEXT: double &_t9 = _t7.value; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t10 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 0, &_d_V2, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t11 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: long double &_t12 = _t10.value; +// CHECK-NEXT: long double &_t13 = _t11.value; +// CHECK-NEXT: double _d_res = _t6.pushforward * _t9 + _t8 * _t7.pushforward + _t10.pushforward * _t13 + _t12 * _t11.pushforward; +// CHECK-NEXT: double res = _t8 * _t9 + _t12 * _t13; +// CHECK-NEXT: return _d_res; +// CHECK-NEXT: } + +double fnVec2(double u, double v) { + const std::vector V1(2, u); + const std::vector V2(2, v); + return V1[0] * V2[1]; +} + +// CHECK: double fnVec2_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, u, {{.*}}0, _d_u{{.*}}); +// CHECK-NEXT: const std::vector _d_V1(_t0.pushforward); +// CHECK-NEXT: const std::vector V1(_t0.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t1 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, v, {{.*}}0, _d_v{{.*}}); +// CHECK-NEXT: const std::vector _d_V2(_t1.pushforward); +// CHECK-NEXT: const std::vector V2(_t1.value); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t3 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: const double _t4 = _t2.value; +// CHECK-NEXT: const double _t5 = _t3.value; +// CHECK-NEXT: return _t2.pushforward * _t5 + _t4 * _t3.pushforward; +// CHECK-NEXT: } + +double fnVec3(double u, double v) { + std::vector V1(2, u); + std::vector &V2 = V1; + return V1[0] + V2[1]; +} + +// CHECK: double fnVec3_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), 2, u, {{.*}}0, _d_u{{.*}}); +// CHECK-NEXT: std::vector _d_V1(_t0.pushforward); +// CHECK-NEXT: std::vector V1(_t0.value); +// CHECK-NEXT: std::vector &_d_V2 = _d_V1; +// CHECK-NEXT: std::vector &V2 = V1; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t1 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V1, 0, &_d_V1, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V2, 1, &_d_V2, 0); +// CHECK-NEXT: return _t1.pushforward + _t2.pushforward; +// CHECK-NEXT: } + +double fnVec4(double u, double v) { + std::vector V{u, v, u * v}; + return V[0] * V[2]; +} + +// CHECK: double fnVec4_darg0(double u, double v) { +// CHECK-NEXT: double _d_u = 1; +// CHECK-NEXT: double _d_v = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = clad::custom_derivatives::class_functions::constructor_pushforward(clad::ConstructorPushforwardTag >(), {u, v, u * v}, {{.*}}{_d_u, _d_v, _d_u * v + u * _d_v}{{.*}}); +// CHECK-NEXT: std::vector _d_V_t0.pushforward; +// CHECK-NEXT: std::vector V_t0.value; +// CHECK-NEXT: {{.*}}ValueAndPushforward _t1 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V, 0, &_d_V, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t2 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&V, 2, &_d_V, 0); +// CHECK-NEXT: double &_t3 = _t1.value; +// CHECK-NEXT: double &_t4 = _t2.value; +// CHECK-NEXT: return _t1.pushforward * _t4 + _t3 * _t2.pushforward; +// CHECK-NEXT: } + +int main() { + INIT_DIFFERENTIATE(fnVec1, "u"); + INIT_DIFFERENTIATE(fnVec2, "u"); + INIT_DIFFERENTIATE(fnVec3, "u"); + INIT_DIFFERENTIATE(fnVec4, "u"); + + TEST_DIFFERENTIATE(fnVec1, 3, 5); // CHECK-EXEC: {10.00} + TEST_DIFFERENTIATE(fnVec2, 3, 5); // CHECK-EXEC: {5.00} + TEST_DIFFERENTIATE(fnVec3, 3, 5); // CHECK-EXEC: {2.00} + TEST_DIFFERENTIATE(fnVec4, 3, 5); // CHECK-EXEC: {30.00} +} \ No newline at end of file diff --git a/test/ForwardMode/UserDefinedTypes.C b/test/ForwardMode/UserDefinedTypes.C index b590a7571..7d5f77b18 100644 --- a/test/ForwardMode/UserDefinedTypes.C +++ b/test/ForwardMode/UserDefinedTypes.C @@ -881,10 +881,10 @@ double fn14(double i, double j) { // CHECK-NEXT: vectorD _d_v; // CHECK-NEXT: vectorD v; // CHECK-NEXT: clad::custom_derivatives::class_functions::resize_pushforward(&v, 5, 0, &_d_v, 0, 0); -// CHECK-NEXT: clad::ValueAndPushforward<{{.*}}, {{.*}}> _t0 = v.operator_subscript_pushforward(0, &_d_v, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}, {{.*}}> _t0 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&v, 0, &_d_v, 0); // CHECK-NEXT: _t0.pushforward = 0 * i + 9 * _d_i; // CHECK-NEXT: _t0.value = 9 * i; -// CHECK-NEXT: clad::ValueAndPushforward<{{.*}}, {{.*}}> _t1 = v.operator_subscript_pushforward(1, &_d_v, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}, {{.*}}> _t1 = clad::custom_derivatives::class_functions::operator_subscript_pushforward(&v, 1, &_d_v, 0); // CHECK-NEXT: _t1.pushforward = 0 * i + 11 * _d_i; // CHECK-NEXT: _t1.value = 11 * i; // CHECK-NEXT: clad::ValueAndPushforward _t2 = begin_pushforward(v, _d_v);