r312085 - Give a better error if auto deduction fails due to inconsistent element types in a braced initializer list.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 29 17:44:08 PDT 2017


Author: rsmith
Date: Tue Aug 29 17:44:08 2017
New Revision: 312085

URL: http://llvm.org/viewvc/llvm-project?rev=312085&view=rev
Log:
Give a better error if auto deduction fails due to inconsistent element types in a braced initializer list.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=312085&r1=312084&r2=312085&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Aug 29 17:44:08 2017
@@ -1984,6 +1984,9 @@ def err_auto_var_deduction_failure_from_
   "cannot deduce actual type for variable %0 with type %1 from initializer list">;
 def err_auto_new_deduction_failure : Error<
   "new expression for type %0 has incompatible constructor argument of type %1">;
+def err_auto_inconsistent_deduction : Error<
+  "deduced conflicting types %diff{($ vs $) |}0,1"
+  "for initializer list element type">;
 def err_auto_different_deductions : Error<
   "%select{'auto'|'decltype(auto)'|'__auto_type'|template arguments}0 "
   "deduced as %1 in declaration of %2 and "

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=312085&r1=312084&r2=312085&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Tue Aug 29 17:44:08 2017
@@ -2907,17 +2907,26 @@ Sema::SubstituteExplicitTemplateArgument
 
 /// \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.
@@ -2941,7 +2950,7 @@ CheckOriginalCallArgDeduction(Sema &S, S
     // 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();
@@ -2961,7 +2970,7 @@ CheckOriginalCallArgDeduction(Sema &S, S
     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
@@ -2982,7 +2991,7 @@ CheckOriginalCallArgDeduction(Sema &S, S
       (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. [...]
@@ -3003,13 +3012,13 @@ CheckOriginalCallArgDeduction(Sema &S, S
   }
 
   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
@@ -3165,13 +3174,9 @@ Sema::TemplateDeductionResult Sema::Fini
         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;
     }
   }
 
@@ -4231,6 +4236,31 @@ Sema::DeduceAutoType(TypeSourceInfo *Typ
                         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
@@ -4318,12 +4348,15 @@ Sema::DeduceAutoType(TypeLoc Type, Expr
 
   // 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;
   };
 
@@ -4337,12 +4370,20 @@ Sema::DeduceAutoType(TypeLoc Type, Expr
     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()) {
@@ -4350,15 +4391,15 @@ Sema::DeduceAutoType(TypeLoc Type, Expr
       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();
 
@@ -4378,9 +4419,10 @@ Sema::DeduceAutoType(TypeLoc Type, Expr
   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, {});
     }
   }
 

Modified: cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp?rev=312085&r1=312084&r2=312085&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx0x-initializer-stdinitializerlist.cpp Tue Aug 29 17:44:08 2017
@@ -136,9 +136,15 @@ void auto_deduction() {
   auto l3 {1};
   static_assert(same_type<decltype(l), std::initializer_list<int>>::value, "");
   static_assert(same_type<decltype(l3), int>::value, "");
-  auto bl = {1, 2.0}; // expected-error {{cannot deduce}}
+  auto bl = {1, 2.0}; // expected-error {{deduced conflicting types ('int' vs 'double') for initializer list element type}}
+
+  void f1(int), f1(float), f2(int), f3(float);
+  auto fil = {f1, f2};
+  auto ffl = {f1, f3};
+  auto fl = {f1, f2, f3}; // expected-error {{deduced conflicting types ('void (*)(int)' vs 'void (*)(float)') for initializer list element type}}
 
   for (int i : {1, 2, 3, 4}) {}
+  for (int j : {1.0, 2.0, 3.0f, 4.0}) {} // expected-error {{deduced conflicting types ('double' vs 'float') for initializer list element type}}
 }
 
 void dangle() {




More information about the cfe-commits mailing list