Skip to content

Commit 885b35b

Browse files
PetroZarytskyivgvassilev
authored andcommitted
Add diagnostics for user-defined getErrorVal functions
1 parent 3ba5d8c commit 885b35b

5 files changed

Lines changed: 162 additions & 64 deletions

File tree

include/clad/Differentiator/CladUtils.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,27 @@ namespace clad {
177177
clang::DeclarationNameInfo BuildDeclarationNameInfo(clang::Sema& S,
178178
llvm::StringRef name);
179179

180+
/// Checks if a set of overloads has one that matches the required type.
181+
///
182+
///\param[in] S reference to `Sema`
183+
///\param[in] FnTy required type
184+
///\param[in] Overloads set of overloads
185+
///\param[in] FailedCandidates set for accumulating failed overload
186+
/// candidates
187+
clang::Expr*
188+
MatchOverloadType(clang::Sema& S, clang::QualType FnTy,
189+
clang::LookupResult& Overloads,
190+
clang::TemplateSpecCandidateSet& FailedCandidates);
191+
192+
/// Produces note-diagnostics about type mismatches between user-provided
193+
/// functions and the required signature.
194+
///
195+
///\param[in] S reference to `Sema`
196+
///\param[in] FnTy required type
197+
///\param[in] Overloads set of overloads
198+
void DiagnoseSignatureMismatch(clang::Sema& S, clang::QualType FnTy,
199+
const clang::LookupResult& Overloads);
200+
180201
/// Returns true if the function has any reference or pointer parameter;
181202
/// otherwise returns false.
182203
bool HasAnyReferenceOrPointerArgument(const clang::FunctionDecl* FD);

lib/Differentiator/CladUtils.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
#include "clang/Analysis/AnalysisDeclContext.h"
1818
#include "clang/Analysis/CFG.h"
1919
#include "clang/Basic/Builtins.h"
20+
#include "clang/Basic/PartialDiagnostic.h"
2021
#include "clang/Basic/SourceLocation.h"
2122
#include "clang/Sema/Lookup.h"
23+
#include "clang/Sema/Sema.h"
24+
#include "clang/Sema/TemplateDeduction.h"
2225

2326
#include "llvm/ADT/SmallVector.h"
2427
#include "llvm/Support/Casting.h"
@@ -947,6 +950,80 @@ namespace clad {
947950
return cast<TemplateDecl>(TapeR.getFoundDecl());
948951
}
949952

953+
Expr* MatchOverloadType(Sema& S, QualType FnTy, LookupResult& Overloads,
954+
TemplateSpecCandidateSet& FailedCandidates) {
955+
CXXScopeSpec SS;
956+
ASTContext& C = S.getASTContext();
957+
// Check if any of the custom derivative signature satisfy the
958+
// requirements.
959+
for (LookupResult::iterator I = Overloads.begin(), E = Overloads.end();
960+
I != E; ++I) {
961+
NamedDecl* candidate = I.getDecl();
962+
// Shadow decls don't provide enough information, go to the actual decl.
963+
if (auto* usingShadow = dyn_cast<UsingShadowDecl>(candidate))
964+
candidate = usingShadow->getTargetDecl();
965+
966+
// Overload is a template, try to match the signature.
967+
if (auto* FTD = dyn_cast<FunctionTemplateDecl>(candidate)) {
968+
FunctionDecl* Specialization = nullptr;
969+
sema::TemplateDeductionInfo Info(FailedCandidates.getLocation());
970+
TemplateArgumentListInfo ExplicitTemplateArgs;
971+
auto TDK = S.DeduceTemplateArguments(FTD, &ExplicitTemplateArgs, FnTy,
972+
Specialization, Info);
973+
974+
// Instantiation with the required signature succeeded.
975+
if (TDK == clad_compat::CLAD_COMPAT_TemplateSuccess)
976+
return S.BuildDeclarationNameExpr(SS, Overloads, /*ADL=*/false)
977+
.get();
978+
979+
FailedCandidates.addCandidate().set(
980+
I.getPair(), FTD->getTemplatedDecl(),
981+
MakeDeductionFailureInfo(C, TDK, Info));
982+
983+
// Instantiation of parameters suceeded but clang doesn't consider
984+
// deduction successful because of the auto return type.
985+
if (Specialization && !Specialization->isTemplated() &&
986+
Specialization->getReturnType()->isUndeducedAutoType())
987+
return S.BuildDeclarationNameExpr(SS, Overloads, /*ADL=*/false)
988+
.get();
989+
}
990+
auto* FD = dyn_cast<FunctionDecl>(candidate);
991+
if (!FD)
992+
continue;
993+
// Overload is just a FunctionDecl, check if the signature matches.
994+
if (C.hasSameFunctionTypeIgnoringExceptionSpec(FD->getType(), FnTy))
995+
return S.BuildDeclarationNameExpr(SS, Overloads, /*ADL=*/false).get();
996+
}
997+
return nullptr;
998+
}
999+
1000+
void DiagnoseSignatureMismatch(Sema& S, QualType FnTy,
1001+
const LookupResult& Overloads) {
1002+
ASTContext& C = S.getASTContext();
1003+
std::string Name = Overloads.getLookupName().getAsString();
1004+
unsigned noteId = S.Diags.getCustomDiagID(
1005+
DiagnosticsEngine::Note,
1006+
"candidate '%0'"
1007+
"%select{| has different class%diff{ (expected $ but has $)|}1,2"
1008+
"| has different number of parameters (expected %2 but has %3)"
1009+
"| has type mismatch at %ordinal2 parameter"
1010+
"%diff{ (expected $ but has $)|}3,4"
1011+
"| has different return type%diff{ ($ expected but has $)|}2,3"
1012+
"| has different qualifiers (expected %2 but found %3)"
1013+
"| has different exception specification}1");
1014+
1015+
for (const NamedDecl* ND : Overloads) {
1016+
if (const auto* usingShadow = dyn_cast<UsingShadowDecl>(ND))
1017+
ND = usingShadow->getTargetDecl();
1018+
if (!isa<FunctionDecl>(ND))
1019+
continue;
1020+
const auto* FD = cast<FunctionDecl>(ND);
1021+
auto PD = PartialDiagnostic(noteId, C.getDiagAllocator()) << Name;
1022+
S.HandleFunctionTypeMismatch(PD, FD->getType(), FnTy);
1023+
S.Diag(FD->getLocation(), PD);
1024+
}
1025+
}
1026+
9501027
bool isMemoryType(QualType T) {
9511028
T = T.getCanonicalType();
9521029
if (T->isReferenceType())

lib/Differentiator/DiffPlanner.cpp

Lines changed: 5 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,7 @@ static QualType GetDerivedFunctionType(const CallExpr* CE) {
987987

988988
std::string Name = R.ComputeDerivativeName();
989989
LookupResult Found = LookupPropagator(Name);
990-
// FIXME: This is a hack to reuse the builtin derivatives for vector mode.
990+
// This is a hack to reuse the builtin derivatives for vector mode.
991991
if (Found.empty() && R.Mode == DiffMode::vector_pushforward)
992992
Found = LookupPropagator(R.BaseFunctionName + "_pushforward");
993993

@@ -996,44 +996,9 @@ static QualType GetDerivedFunctionType(const CallExpr* CE) {
996996

997997
TemplateSpecCandidateSet FailedCandidates(R.CallContext->getBeginLoc(),
998998
/*ForTakingAddress=*/false);
999-
CXXScopeSpec SS;
1000-
// Check if any of the custom derivative signature satisfy the requirements.
1001-
for (LookupResult::iterator I = Found.begin(), E = Found.end(); I != E;
1002-
++I) {
1003-
NamedDecl* candidate = I.getDecl();
1004-
// Shadow decls don't provide enough information, go to the actual decl.
1005-
if (auto* usingShadow = dyn_cast<UsingShadowDecl>(candidate))
1006-
candidate = usingShadow->getTargetDecl();
1007-
1008-
// Overload is a template, try to match the signature.
1009-
if (auto* FTD = dyn_cast<FunctionTemplateDecl>(candidate)) {
1010-
FunctionDecl* Specialization = nullptr;
1011-
sema::TemplateDeductionInfo Info(FailedCandidates.getLocation());
1012-
TemplateArgumentListInfo ExplicitTemplateArgs;
1013-
auto TDK = S.DeduceTemplateArguments(FTD, &ExplicitTemplateArgs, dTy,
1014-
Specialization, Info);
1015-
1016-
// Instantiation with the required signature succeeded.
1017-
if (TDK == clad_compat::CLAD_COMPAT_TemplateSuccess)
1018-
return S.BuildDeclarationNameExpr(SS, Found, /*ADL=*/false).get();
1019-
1020-
FailedCandidates.addCandidate().set(
1021-
I.getPair(), FTD->getTemplatedDecl(),
1022-
MakeDeductionFailureInfo(C, TDK, Info));
1023-
1024-
// Instantiation of parameters suceeded but clang doesn't consider
1025-
// deduction successful because of the auto return type.
1026-
if (Specialization && !Specialization->isTemplated() &&
1027-
Specialization->getReturnType()->isUndeducedAutoType())
1028-
return S.BuildDeclarationNameExpr(SS, Found, /*ADL=*/false).get();
1029-
}
1030-
auto* FD = dyn_cast<FunctionDecl>(candidate);
1031-
if (!FD)
1032-
continue;
1033-
// Overload is just a FunctionDecl, check if the signature matches.
1034-
if (C.hasSameFunctionTypeIgnoringExceptionSpec(FD->getType(), dTy))
1035-
return S.BuildDeclarationNameExpr(SS, Found, /*ADL=*/false).get();
1036-
}
999+
if (Expr* overload =
1000+
utils::MatchOverloadType(S, dTy, Found, FailedCandidates))
1001+
return overload;
10371002

10381003
if (!enableDiagnostics)
10391004
return nullptr;
@@ -1045,28 +1010,8 @@ static QualType GetDerivedFunctionType(const CallExpr* CE) {
10451010
"expected signature %1 does not match");
10461011
S.Diag(R.CallContext->getBeginLoc(), errId) << R.Function << dTy;
10471012
FailedCandidates.NoteCandidates(S, R.CallContext->getBeginLoc());
1013+
utils::DiagnoseSignatureMismatch(S, dTy, Found);
10481014

1049-
unsigned noteId = S.Diags.getCustomDiagID(
1050-
DiagnosticsEngine::Note,
1051-
"candidate '%0'"
1052-
"%select{| has different class%diff{ (expected $ but has $)|}1,2"
1053-
"| has different number of parameters (expected %2 but has %3)"
1054-
"| has type mismatch at %ordinal2 parameter"
1055-
"%diff{ (expected $ but has $)|}3,4"
1056-
"| has different return type%diff{ ($ expected but has $)|}2,3"
1057-
"| has different qualifiers (expected %2 but found %3)"
1058-
"| has different exception specification}1");
1059-
1060-
for (const NamedDecl* ND : Found) {
1061-
if (const auto* usingShadow = dyn_cast<UsingShadowDecl>(ND))
1062-
ND = usingShadow->getTargetDecl();
1063-
if (!isa<FunctionDecl>(ND))
1064-
continue;
1065-
const auto* FD = cast<FunctionDecl>(ND);
1066-
auto PD = PartialDiagnostic(noteId, C.getDiagAllocator()) << Name;
1067-
S.HandleFunctionTypeMismatch(PD, FD->getType(), dTy);
1068-
S.Diag(FD->getLocation(), PD);
1069-
}
10701015
return nullptr;
10711016
}
10721017

lib/Differentiator/EstimationModel.cpp

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
#include "clang/AST/DeclarationName.h"
1010
#include "clang/AST/Expr.h"
1111
#include "clang/AST/OperationKinds.h"
12+
#include "clang/AST/Type.h"
1213
#include "clang/Sema/DeclSpec.h"
1314
#include "clang/Sema/Lookup.h"
15+
#include "clang/Sema/TemplateDeduction.h"
1416

1517
#include "llvm/ADT/APFloat.h"
1618
#include "llvm/ADT/SmallVector.h"
@@ -28,18 +30,41 @@ FPErrorEstimationModel::FPErrorEstimationModel(DerivativeBuilder& builder,
2830
}
2931

3032
void FPErrorEstimationModel::LookupCustomErrorFunction() {
31-
CXXScopeSpec SS;
3233
NamespaceDecl* cladNS =
3334
utils::LookupNSD(m_Sema, "clad", /*shouldExist=*/true);
34-
SS.Extend(m_Context, cladNS, noLoc, noLoc);
3535
IdentifierInfo* II = &m_Context.Idents.get("getErrorVal");
3636
DeclarationNameInfo DNInfo(DeclarationName(II), utils::GetValidSLoc(m_Sema));
3737
LookupResult R(m_Sema, DNInfo, Sema::LookupOrdinaryName);
3838
m_Sema.LookupQualifiedName(R, cladNS);
3939
if (R.empty())
4040
return;
41-
m_CustomErrorFunction =
42-
m_Sema.BuildDeclarationNameExpr(SS, R, /*ADL*/ false).get();
41+
42+
FunctionProtoType::ExtProtoInfo EPI;
43+
QualType ConstCharPtr =
44+
m_Context.getPointerType(m_Context.getConstType(m_Context.CharTy));
45+
QualType DoubleTy = m_Context.DoubleTy;
46+
llvm::SmallVector<QualType, 3> FnTypes = {DoubleTy, DoubleTy, ConstCharPtr};
47+
QualType FnTy = m_Context.getFunctionType(DoubleTy, FnTypes, EPI);
48+
TemplateSpecCandidateSet FailedCandidates(utils::GetValidSLoc(m_Sema),
49+
/*ForTakingAddress=*/false);
50+
if (utils::MatchOverloadType(m_Sema, FnTy, R, FailedCandidates)) {
51+
// FIXME: MatchOverloadType returns an overload expr without the `clad::`
52+
// namespace specifier. Here, we rebuild manually.
53+
CXXScopeSpec SS;
54+
SS.Extend(m_Context, cladNS, noLoc, noLoc);
55+
m_CustomErrorFunction =
56+
m_Sema.BuildDeclarationNameExpr(SS, R, /*ADL=*/false).get();
57+
return;
58+
}
59+
60+
// We did not match the found candidates. Warn and offer the user hints.
61+
auto errId = m_Sema.Diags.getCustomDiagID(
62+
DiagnosticsEngine::Error,
63+
"user-defined derivative error function was provided but not used; "
64+
"expected signature %0 does not match");
65+
m_Sema.Diag(m_DiffReq.Function->getLocation(), errId) << FnTy;
66+
FailedCandidates.NoteCandidates(m_Sema, utils::GetValidSLoc(m_Sema));
67+
utils::DiagnoseSignatureMismatch(m_Sema, FnTy, R);
4368
}
4469

4570
Expr* FPErrorEstimationModel::AssignError(StmtDiff refExpr,

test/ErrorEstimation/Diagnostics.C

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %cladclang %s -I%S/../../include -fsyntax-only -Xclang -verify
2+
3+
#include "clad/Differentiator/Differentiator.h"
4+
5+
#include <string>
6+
7+
namespace clad {
8+
double getErrorVal(double dx, double x) { // expected-note {{candidate 'getErrorVal' has different number of parameters (expected 3 but has 2)}}
9+
return dx * x;
10+
}
11+
template<typename T>
12+
T getErrorVal(T dx, T x, T name) { // expected-note {{candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'const char *')}}
13+
return dx * x;
14+
}
15+
float getErrorVal(double dx, double x, const char* name) { // expected-note {{candidate 'getErrorVal' has different return type ('double' expected but has 'float')}}
16+
return dx * x;
17+
}
18+
double getErrorVal(double dx, double x, std::string name) { // expected-note {{candidate 'getErrorVal' has type mismatch at 3rd parameter (expected 'const char *' but has 'std::string' (aka 'basic_string<char>'))}}
19+
return dx * x;
20+
}
21+
} // namespace clad
22+
23+
// Add/Sub operations
24+
float f1(float x, float y) { // expected-error {{user-defined derivative error function was provided but not used; expected signature 'double (double, double, const char *)' does not match}}
25+
return x + y;
26+
}
27+
28+
int main() {
29+
clad::estimate_error(f1);
30+
}

0 commit comments

Comments
 (0)