/// \brief Check whether the deduced argument type for a call to a function
/// template matches the actual argument type per C++ [temp.deduct.call]p4.
-static bool
-CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg,
+static Sema::TemplateDeductionResult
+CheckOriginalCallArgDeduction(Sema &S, TemplateDeductionInfo &Info,
+ Sema::OriginalCallArg OriginalArg,
QualType DeducedA) {
ASTContext &Context = S.Context;
+ auto Failed = [&]() -> Sema::TemplateDeductionResult {
+ Info.FirstArg = TemplateArgument(DeducedA);
+ Info.SecondArg = TemplateArgument(OriginalArg.OriginalArgType);
+ Info.CallArgIndex = OriginalArg.ArgIdx;
+ return OriginalArg.DecomposedParam ? Sema::TDK_DeducedMismatchNested
+ : Sema::TDK_DeducedMismatch;
+ };
+
QualType A = OriginalArg.OriginalArgType;
QualType OriginalParamType = OriginalArg.OriginalParamType;
// Check for type equality (top-level cv-qualifiers are ignored).
if (Context.hasSameUnqualifiedType(A, DeducedA))
- return false;
+ return Sema::TDK_Success;
// Strip off references on the argument types; they aren't needed for
// the following checks.
// the deduced A can be F.
QualType Tmp;
if (A->isFunctionType() && S.IsFunctionConversion(A, DeducedA, Tmp))
- return false;
+ return Sema::TDK_Success;
Qualifiers AQuals = A.getQualifiers();
Qualifiers DeducedAQuals = DeducedA.getQualifiers();
if (AQuals == DeducedAQuals) {
// Qualifiers match; there's nothing to do.
} else if (!DeducedAQuals.compatiblyIncludes(AQuals)) {
- return true;
+ return Failed();
} else {
// Qualifiers are compatible, so have the argument type adopt the
// deduced argument type's qualifiers as if we had performed the
(S.IsQualificationConversion(A, DeducedA, false,
ObjCLifetimeConversion) ||
S.IsFunctionConversion(A, DeducedA, ResultTy)))
- return false;
+ return Sema::TDK_Success;
// - If P is a class and P has the form simple-template-id, then the
// transformed A can be a derived class of the deduced A. [...]
}
if (Context.hasSameUnqualifiedType(A, DeducedA))
- return false;
+ return Sema::TDK_Success;
if (A->isRecordType() && isSimpleTemplateIdType(OriginalParamType) &&
S.IsDerivedFrom(SourceLocation(), A, DeducedA))
- return false;
+ return Sema::TDK_Success;
- return true;
+ return Failed();
}
/// Find the pack index for a particular parameter index in an instantiation of
DeducedA = CacheEntry;
}
- if (CheckOriginalCallArgDeduction(*this, OriginalArg, DeducedA)) {
- Info.FirstArg = TemplateArgument(DeducedA);
- Info.SecondArg = TemplateArgument(OriginalArg.OriginalArgType);
- Info.CallArgIndex = OriginalArg.ArgIdx;
- return OriginalArg.DecomposedParam ? TDK_DeducedMismatchNested
- : TDK_DeducedMismatch;
- }
+ if (auto TDK =
+ CheckOriginalCallArgDeduction(*this, Info, OriginalArg, DeducedA))
+ return TDK;
}
}
DependentDeductionDepth);
}
+/// Attempt to produce an informative diagostic explaining why auto deduction
+/// failed.
+/// \return \c true if diagnosed, \c false if not.
+static bool diagnoseAutoDeductionFailure(Sema &S,
+ Sema::TemplateDeductionResult TDK,
+ TemplateDeductionInfo &Info,
+ ArrayRef<SourceRange> Ranges) {
+ switch (TDK) {
+ case Sema::TDK_Inconsistent: {
+ // Inconsistent deduction means we were deducing from an initializer list.
+ auto D = S.Diag(Info.getLocation(), diag::err_auto_inconsistent_deduction);
+ D << Info.FirstArg << Info.SecondArg;
+ for (auto R : Ranges)
+ D << R;
+ return true;
+ }
+
+ // FIXME: Are there other cases for which a custom diagnostic is more useful
+ // than the basic "types don't match" diagnostic?
+
+ default:
+ return false;
+ }
+}
+
/// \brief Deduce the type for an auto type-specifier (C++11 [dcl.spec.auto]p6)
///
/// Note that this is done even if the initializer is dependent. (This is
// If deduction failed, don't diagnose if the initializer is dependent; it
// might acquire a matching type in the instantiation.
- auto DeductionFailed = [&]() -> DeduceAutoResult {
+ auto DeductionFailed = [&](TemplateDeductionResult TDK,
+ ArrayRef<SourceRange> Ranges) -> DeduceAutoResult {
if (Init->isTypeDependent()) {
Result = SubstituteDeducedTypeTransform(*this, QualType()).Apply(Type);
assert(!Result.isNull() && "substituting DependentTy can't fail");
return DAR_Succeeded;
}
+ if (diagnoseAutoDeductionFailure(*this, TDK, Info, Ranges))
+ return DAR_FailedAlreadyDiagnosed;
return DAR_Failed;
};
if (!Type.getType().getNonReferenceType()->getAs<AutoType>())
return DAR_Failed;
+ SourceRange DeducedFromInitRange;
for (unsigned i = 0, e = InitList->getNumInits(); i < e; ++i) {
- if (DeduceTemplateArgumentsFromCallArgument(
- *this, TemplateParamsSt.get(), 0, TemplArg, InitList->getInit(i),
+ Expr *Init = InitList->getInit(i);
+
+ if (auto TDK = DeduceTemplateArgumentsFromCallArgument(
+ *this, TemplateParamsSt.get(), 0, TemplArg, Init,
Info, Deduced, OriginalCallArgs, /*Decomposed*/ true,
/*ArgIdx*/ 0, /*TDF*/ 0))
- return DeductionFailed();
+ return DeductionFailed(TDK, {DeducedFromInitRange,
+ Init->getSourceRange()});
+
+ if (DeducedFromInitRange.isInvalid() &&
+ Deduced[0].getKind() != TemplateArgument::Null)
+ DeducedFromInitRange = Init->getSourceRange();
}
} else {
if (!getLangOpts().CPlusPlus && Init->refersToBitField()) {
return DAR_FailedAlreadyDiagnosed;
}
- if (DeduceTemplateArgumentsFromCallArgument(
+ if (auto TDK = DeduceTemplateArgumentsFromCallArgument(
*this, TemplateParamsSt.get(), 0, FuncParam, Init, Info, Deduced,
OriginalCallArgs, /*Decomposed*/ false, /*ArgIdx*/ 0, /*TDF*/ 0))
- return DeductionFailed();
+ return DeductionFailed(TDK, {});
}
// Could be null if somehow 'auto' appears in a non-deduced context.
if (Deduced[0].getKind() != TemplateArgument::Type)
- return DeductionFailed();
+ return DeductionFailed(TDK_Incomplete, {});
QualType DeducedType = Deduced[0].getAsType();
for (const OriginalCallArg &OriginalArg : OriginalCallArgs) {
assert((bool)InitList == OriginalArg.DecomposedParam &&
"decomposed non-init-list in auto deduction?");
- if (CheckOriginalCallArgDeduction(*this, OriginalArg, DeducedA)) {
+ if (auto TDK =
+ CheckOriginalCallArgDeduction(*this, Info, OriginalArg, DeducedA)) {
Result = QualType();
- return DeductionFailed();
+ return DeductionFailed(TDK, {});
}
}