[cfe-commits] r145114 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Sema/Sema.h lib/Sema/SemaExpr.cpp lib/Sema/SemaInit.cpp lib/Sema/SemaOverload.cpp lib/Sema/SemaTemplateDeduction.cpp test/SemaCXX/addr-of-overloaded-function.cpp

Richard Trieu rtrieu at google.com
Wed Nov 23 14:32:32 PST 2011


Author: rtrieu
Date: Wed Nov 23 16:32:32 2011
New Revision: 145114

URL: http://llvm.org/viewvc/llvm-project?rev=145114&view=rev
Log:
Add feature to diagnostics that will provide more information on function
pointer mismatch.  Cases covered are: initialization, assignment, and function
arguments.  Additional text will give the extra information about the nature
of the mismatch: different classes for member functions, wrong number of
parameters, different parameter type, different return type, and function
qualifier mismatch.

Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaInit.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
    cfe/trunk/test/SemaCXX/addr-of-overloaded-function.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=145114&r1=145113&r2=145114&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Nov 23 16:32:32 2011
@@ -1014,7 +1014,16 @@
   "cannot initialize %select{a variable|a parameter|return object|an "
   "exception object|a member subobject|an array element|a new value|a value|a "
   "base class|a constructor delegation|a vector element}0 of type %1 with an "
-  "%select{rvalue|lvalue}2 of type %3">;
+  "%select{rvalue|lvalue}2 of type %3"
+  "%select{|: different classes (%5 vs %6)"
+  "|: different number of parameters (%5 vs %6)"
+  "|: type mismatch in %ordinal5 parameter (%6 vs %7)"
+  "|: different return type (%5 vs %6)"
+  "|: different qualifiers ("
+  "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
+  "volatile and restrict|const, volatile, and restrict}5 vs "
+  "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
+  "volatile and restrict|const, volatile, and restrict}6)}4">;
 
 def err_lvalue_to_rvalue_ref : Error<"rvalue reference to type %0 cannot bind "
   "to lvalue of type %1">;
@@ -1823,7 +1832,16 @@
     "is the implicit move constructor|"
     "is the implicit copy assignment operator|"
     "is the implicit move assignment operator|"
-    "is an inherited constructor}0%1">;
+    "is an inherited constructor}0%1"
+    "%select{| has different class (expected %3 but has %4)"
+    "| has different number of parameters (expected %3 but has %4)"
+    "| has type mismatch in %ordinal3 parameter (expected %4 but has %5)"
+    "| has different return type (%3 expected but has %4)"
+    "| has different qualifiers (expected "
+    "%select{none|const|restrict|const and restrict|volatile|const and volatile"
+    "|volatile and restrict|const, volatile, and restrict}3 but found "
+    "%select{none|const|restrict|const and restrict|volatile|const and volatile"
+    "|volatile and restrict|const, volatile, and restrict}4)}2">;
 
 def note_ovl_candidate_inherited_constructor : Note<"inherited from here">;
 def note_ovl_candidate_bad_deduction : Note<
@@ -4022,11 +4040,21 @@
   "%select{from incompatible type|to parameter of incompatible type|"
   "from a function with incompatible result type|to incompatible type|"
   "with an expression of incompatible type|to parameter of incompatible type|"
-  "to incompatible type}2 %1; "
-  "%select{|dereference with *|"
-  "take the address with &|"
-  "remove *|"
-  "remove &}3">;
+  "to incompatible type}2 %1"
+  "%select{|; dereference with *|"
+  "; take the address with &|"
+  "; remove *|"
+  "; remove &}3"
+  "%select{|: different classes (%5 vs %6)"
+  "|: different number of parameters (%5 vs %6)"
+  "|: type mismatch at %ordinal5 parameter (%6 vs %7)"
+  "|: different return type (%5 vs %6)"
+  "|: different qualifiers ("
+  "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
+  "volatile and restrict|const, volatile, and restrict}5 vs "
+  "%select{none|const|restrict|const and restrict|volatile|const and volatile|"
+  "volatile and restrict|const, volatile, and restrict}6)}4">;
+
 def warn_incompatible_qualified_id : Warning<
   "%select{assigning to|passing|returning|converting|initializing|sending|casting}2"
   " %0 "

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=145114&r1=145113&r2=145114&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Nov 23 16:32:32 2011
@@ -1399,7 +1399,10 @@
   bool IsBlockPointerConversion(QualType FromType, QualType ToType,
                                 QualType& ConvertedType);
   bool FunctionArgTypesAreEqual(const FunctionProtoType *OldType,
-                                const FunctionProtoType *NewType);
+                                const FunctionProtoType *NewType,
+                                unsigned *ArgPos = 0);
+  void HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
+                                  QualType FromType, QualType ToType);
 
   CastKind PrepareCastToObjCObjectPointer(ExprResult &E);
   bool CheckPointerConversion(Expr *From, QualType ToType,
@@ -1542,11 +1545,11 @@
                                         bool StdNamespaceIsAssociated = false);
 
   // Emit as a 'note' the specific overload candidate
-  void NoteOverloadCandidate(FunctionDecl *Fn);
+  void NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType = QualType());
 
   // Emit as a series of 'note's all template and non-templates
   // identified by the expression Expr
-  void NoteAllOverloadCandidates(Expr* E);
+  void NoteAllOverloadCandidates(Expr* E, QualType DestType = QualType());
 
   // [PossiblyAFunctionType]  -->   [Return]
   // NonFunctionType --> NonFunctionType
@@ -4642,7 +4645,8 @@
                                            const PartialDiagnostic &NoneDiag,
                                            const PartialDiagnostic &AmbigDiag,
                                         const PartialDiagnostic &CandidateDiag,
-                                        bool Complain = true);
+                                        bool Complain = true,
+                                        QualType TargetType = QualType());
 
   ClassTemplatePartialSpecializationDecl *
   getMoreSpecializedPartialSpecialization(

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=145114&r1=145113&r2=145114&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Wed Nov 23 16:32:32 2011
@@ -9028,6 +9028,7 @@
   FixItHint Hint;
   ConversionFixItGenerator ConvHints;
   bool MayHaveConvFixit = false;
+  bool MayHaveFunctionDiff = false;
 
   switch (ConvTy) {
   default: llvm_unreachable("Unknown conversion type");
@@ -9117,6 +9118,7 @@
     ConvHints.tryToFixConversion(SrcExpr, SrcType, DstType, *this);
     MayHaveConvFixit = true;
     isInvalid = true;
+    MayHaveFunctionDiff = true;
     break;
   }
 
@@ -9155,8 +9157,15 @@
   }
   if (MayHaveConvFixit) { FDiag << (unsigned) (ConvHints.Kind); }
 
+  if (MayHaveFunctionDiff)
+    HandleFunctionTypeMismatch(FDiag, SecondType, FirstType);
+
   Diag(Loc, FDiag);
 
+  if (SecondType == Context.OverloadTy)
+    NoteAllOverloadCandidates(OverloadExpr::find(SrcExpr).Expression,
+                              FirstType);
+
   if (CheckInferredResultType)
     EmitRelatedResultTypeNote(SrcExpr);
   

Modified: cfe/trunk/lib/Sema/SemaInit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaInit.cpp?rev=145114&r1=145113&r2=145114&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaInit.cpp (original)
+++ cfe/trunk/lib/Sema/SemaInit.cpp Wed Nov 23 16:32:32 2011
@@ -5118,12 +5118,14 @@
 
   case FK_ConversionFailed: {
     QualType FromType = Args[0]->getType();
-    S.Diag(Kind.getLocation(), diag::err_init_conversion_failed)
+    PartialDiagnostic PDiag = S.PDiag(diag::err_init_conversion_failed)
       << (int)Entity.getKind()
       << DestType
       << Args[0]->isLValue()
       << FromType
       << Args[0]->getSourceRange();
+    S.HandleFunctionTypeMismatch(PDiag, FromType, DestType);
+    S.Diag(Kind.getLocation(), PDiag);
     if (DestType.getNonReferenceType()->isObjCObjectPointerType() &&
         Args[0]->getType()->isObjCObjectPointerType())
       S.EmitRelatedResultTypeNote(Args[0]);

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=145114&r1=145113&r2=145114&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Wed Nov 23 16:32:32 2011
@@ -2163,22 +2163,126 @@
    return true;
 }
 
+enum {
+  ft_default,
+  ft_different_class,
+  ft_parameter_arity,
+  ft_parameter_mismatch,
+  ft_return_type,
+  ft_qualifer_mismatch
+};
+
+/// HandleFunctionTypeMismatch - Gives diagnostic information for differeing
+/// function types.  Catches different number of parameter, mismatch in
+/// parameter types, and different return types.
+void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
+                                      QualType FromType, QualType ToType) {
+  // Get the function type from the pointers.
+  if (FromType->isMemberPointerType() && ToType->isMemberPointerType()) {
+    const MemberPointerType *FromMember = FromType->getAs<MemberPointerType>(),
+                            *ToMember = ToType->getAs<MemberPointerType>();
+    if (FromMember->getClass() != ToMember->getClass()) {
+      PDiag << ft_different_class << QualType(ToMember->getClass(), 0)
+            << QualType(FromMember->getClass(), 0);
+      return;
+    }
+    FromType = FromMember->getPointeeType();
+    ToType = ToMember->getPointeeType();
+  } else if (FromType->isPointerType() && ToType->isPointerType()) {
+    FromType = FromType->getPointeeType();
+    ToType = ToType->getPointeeType();
+  } else {
+    PDiag << ft_default;
+    return;
+  }
+
+  FromType = FromType.getNonReferenceType();
+  ToType = ToType.getNonReferenceType();
+
+  // If either type is not valid, of the types are the same, no extra info.
+  if (FromType.isNull() || ToType.isNull() ||
+      Context.hasSameType(FromType, ToType)) {
+    PDiag << ft_default;
+    return;
+  }
+
+  // Don't print extra info for non-specialized template functions.
+  if (FromType->isInstantiationDependentType() &&
+      !FromType->getAs<TemplateSpecializationType>()) {
+    PDiag << ft_default;
+    return;
+  }
+
+  const FunctionProtoType *FromFunction = FromType->getAs<FunctionProtoType>(),
+                          *ToFunction = ToType->getAs<FunctionProtoType>();
+
+  // Both types need to be function types.
+  if (!FromFunction || !ToFunction) {
+    PDiag << ft_default;
+    return;
+  }
+
+  if (FromFunction->getNumArgs() != ToFunction->getNumArgs()) {
+    PDiag << ft_parameter_arity << ToFunction->getNumArgs()
+          << FromFunction->getNumArgs();
+    return;
+  }
+
+  // Handle different parameter types.
+  unsigned ArgPos;
+  if (!FunctionArgTypesAreEqual(FromFunction, ToFunction, &ArgPos)) {
+    PDiag << ft_parameter_mismatch << ArgPos + 1
+          << ToFunction->getArgType(ArgPos)
+          << FromFunction->getArgType(ArgPos);
+    return;
+  }
+
+  // Handle different return type.
+  if (!Context.hasSameType(FromFunction->getResultType(),
+                           ToFunction->getResultType())) {
+    PDiag << ft_return_type << ToFunction->getResultType()
+          << FromFunction->getResultType();
+    return;
+  }
+
+  unsigned FromQuals = FromFunction->getTypeQuals(),
+           ToQuals = ToFunction->getTypeQuals();
+  if (FromQuals != ToQuals) {
+    PDiag << ft_qualifer_mismatch << ToQuals << FromQuals;
+    return;
+  }
+
+  // Unable to find a difference, so add no extra info.
+  PDiag << ft_default;
+}
+
 /// FunctionArgTypesAreEqual - This routine checks two function proto types
 /// for equlity of their argument types. Caller has already checked that
 /// they have same number of arguments. This routine assumes that Objective-C
 /// pointer types which only differ in their protocol qualifiers are equal.
+/// If the parameters are different, ArgPos will have the the parameter index
+/// of the first different parameter.
 bool Sema::FunctionArgTypesAreEqual(const FunctionProtoType *OldType,
-                                    const FunctionProtoType *NewType) {
-  if (!getLangOptions().ObjC1)
-    return std::equal(OldType->arg_type_begin(), OldType->arg_type_end(),
-                      NewType->arg_type_begin());
+                                    const FunctionProtoType *NewType,
+                                    unsigned *ArgPos) {
+  if (!getLangOptions().ObjC1) {
+    for (FunctionProtoType::arg_type_iterator O = OldType->arg_type_begin(),
+         N = NewType->arg_type_begin(),
+         E = OldType->arg_type_end(); O && (O != E); ++O, ++N) {
+      if (!Context.hasSameType(*O, *N)) {
+        if (ArgPos) *ArgPos = O - OldType->arg_type_begin();
+        return false;
+      }
+    }
+    return true;
+  }
 
   for (FunctionProtoType::arg_type_iterator O = OldType->arg_type_begin(),
        N = NewType->arg_type_begin(),
        E = OldType->arg_type_end(); O && (O != E); ++O, ++N) {
     QualType ToType = (*O);
     QualType FromType = (*N);
-    if (ToType != FromType) {
+    if (!Context.hasSameType(ToType, FromType)) {
       if (const PointerType *PTTo = ToType->getAs<PointerType>()) {
         if (const PointerType *PTFr = FromType->getAs<PointerType>())
           if ((PTTo->getPointeeType()->isObjCQualifiedIdType() &&
@@ -2194,6 +2298,7 @@
           if (PTTo->getInterfaceDecl() == PTFr->getInterfaceDecl())
             continue;
       }
+      if (ArgPos) *ArgPos = O - OldType->arg_type_begin();
       return false;
     }
   }
@@ -7015,17 +7120,19 @@
 } // end anonymous namespace
 
 // Notes the location of an overload candidate.
-void Sema::NoteOverloadCandidate(FunctionDecl *Fn) {
+void Sema::NoteOverloadCandidate(FunctionDecl *Fn, QualType DestType) {
   std::string FnDesc;
   OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Fn, FnDesc);
-  Diag(Fn->getLocation(), diag::note_ovl_candidate)
-    << (unsigned) K << FnDesc;
+  PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
+                             << (unsigned) K << FnDesc;
+  HandleFunctionTypeMismatch(PD, Fn->getType(), DestType);
+  Diag(Fn->getLocation(), PD);
   MaybeEmitInheritedConstructorNote(*this, Fn);
 }
 
 //Notes the location of all overload candidates designated through 
 // OverloadedExpr
-void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr) {
+void Sema::NoteAllOverloadCandidates(Expr* OverloadedExpr, QualType DestType) {
   assert(OverloadedExpr->getType() == Context.OverloadTy);
 
   OverloadExpr::FindResult Ovl = OverloadExpr::find(OverloadedExpr);
@@ -7036,10 +7143,10 @@
        I != IEnd; ++I) {
     if (FunctionTemplateDecl *FunTmpl = 
                 dyn_cast<FunctionTemplateDecl>((*I)->getUnderlyingDecl()) ) {
-      NoteOverloadCandidate(FunTmpl->getTemplatedDecl());   
+      NoteOverloadCandidate(FunTmpl->getTemplatedDecl(), DestType);
     } else if (FunctionDecl *Fun 
                       = dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()) ) {
-      NoteOverloadCandidate(Fun);
+      NoteOverloadCandidate(Fun, DestType);
     }
   }
 }
@@ -8132,7 +8239,7 @@
                              << Matches[0].second->getDeclName(),
                            S.PDiag(diag::note_ovl_candidate)
                              << (unsigned) oc_function_template,
-                           Complain);
+                           Complain, TargetFunctionType);
 
     if (Result != MatchesCopy.end()) {
       // Make it the first and only element
@@ -8161,7 +8268,7 @@
     S.Diag(OvlExpr->getLocStart(), diag::err_addr_ovl_no_viable)
         << OvlExpr->getName() << TargetFunctionType
         << OvlExpr->getSourceRange();
-    S.NoteAllOverloadCandidates(OvlExpr);
+    S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType);
   } 
   
   bool IsInvalidFormOfPointerToMemberFunction() const {
@@ -8187,7 +8294,7 @@
     S.Diag(OvlExpr->getLocStart(), diag::err_addr_ovl_ambiguous)
       << OvlExpr->getName()
       << OvlExpr->getSourceRange();
-    S.NoteAllOverloadCandidates(OvlExpr);
+    S.NoteAllOverloadCandidates(OvlExpr, TargetFunctionType);
   }
 
   bool hadMultipleCandidates() const { return (OvlExpr->getNumDecls() > 1); }

Modified: cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp?rev=145114&r1=145113&r2=145114&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateDeduction.cpp Wed Nov 23 16:32:32 2011
@@ -3783,7 +3783,8 @@
                          const PartialDiagnostic &NoneDiag,
                          const PartialDiagnostic &AmbigDiag,
                          const PartialDiagnostic &CandidateDiag,
-                         bool Complain) {
+                         bool Complain,
+                         QualType TargetType) {
   if (SpecBegin == SpecEnd) {
     if (Complain)
       Diag(Loc, NoneDiag);
@@ -3837,11 +3838,16 @@
 
   if (Complain)
   // FIXME: Can we order the candidates in some sane way?
-    for (UnresolvedSetIterator I = SpecBegin; I != SpecEnd; ++I)
-      Diag((*I)->getLocation(), CandidateDiag)
-        << getTemplateArgumentBindingsText(
+    for (UnresolvedSetIterator I = SpecBegin; I != SpecEnd; ++I) {
+      PartialDiagnostic PD = CandidateDiag;
+      PD << getTemplateArgumentBindingsText(
           cast<FunctionDecl>(*I)->getPrimaryTemplate()->getTemplateParameters(),
                     *cast<FunctionDecl>(*I)->getTemplateSpecializationArgs());
+      if (!TargetType.isNull())
+        HandleFunctionTypeMismatch(PD, cast<FunctionDecl>(*I)->getType(),
+                                   TargetType);
+      Diag((*I)->getLocation(), PD);
+    }
 
   return SpecEnd;
 }

Modified: cfe/trunk/test/SemaCXX/addr-of-overloaded-function.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/addr-of-overloaded-function.cpp?rev=145114&r1=145113&r2=145114&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/addr-of-overloaded-function.cpp (original)
+++ cfe/trunk/test/SemaCXX/addr-of-overloaded-function.cpp Wed Nov 23 16:32:32 2011
@@ -145,3 +145,64 @@
     bar2();
   }
 }
+
+namespace test1 {
+  void fun(int x) {}
+
+  void parameter_number() {
+    void (*ptr1)(int, int) = &fun; // expected-error {{cannot initialize a variable of type 'void (*)(int, int)' with an rvalue of type 'void (*)(int)': different number of parameters (2 vs 1)}}
+    void (*ptr2)(int, int);
+    ptr2 = &fun;  // expected-error {{assigning to 'void (*)(int, int)' from incompatible type 'void (*)(int)': different number of parameters (2 vs 1)}}
+  }
+
+  void parameter_mismatch() {
+    void (*ptr1)(double) = &fun; // expected-error {{cannot initialize a variable of type 'void (*)(double)' with an rvalue of type 'void (*)(int)': type mismatch in 1st parameter ('double' vs 'int')}}
+    void (*ptr2)(double);
+    ptr2 = &fun; // expected-error {{assigning to 'void (*)(double)' from incompatible type 'void (*)(int)': type mismatch at 1st parameter ('double' vs 'int')}}
+  }
+
+  void return_type_test() {
+    int (*ptr1)(int) = &fun; // expected-error {{cannot initialize a variable of type 'int (*)(int)' with an rvalue of type 'void (*)(int)': different return type ('int' vs 'void')}}
+    int (*ptr2)(int);
+    ptr2 = &fun;  // expected-error {{assigning to 'int (*)(int)' from incompatible type 'void (*)(int)': different return type ('int' vs 'void')}}
+  }
+
+  int foo(double x, double y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}}
+  int foo(int x, int y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}}
+  int foo(double x) {return 0;} // expected-note {{candidate function has type mismatch at 1st parameter (expected 'int' but has 'double')}}
+  double foo(float x, float y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}}
+  double foo(int x, float y) {return 0;} // expected-note {{candidate function has different number of parameters (expected 1 but has 2)}}
+  double foo(float x) {return 0;} // expected-note {{candidate function has type mismatch at 1st parameter (expected 'int' but has 'float')}}
+  double foo(int x) {return 0;} // expected-note {{candidate function has different return type ('int' expected but has 'double')}}
+  
+  int (*ptr)(int) = &foo; // expected-error {{address of overloaded function 'foo' does not match required type 'int (int)'}}
+
+  struct Qualifiers {
+    void N() {};
+    void C() const {};
+    void V() volatile {};
+    void R() __restrict {};
+    void CV() const volatile {};
+    void CR() const __restrict {};
+    void VR() volatile __restrict {};
+    void CVR() const volatile __restrict {};
+  };
+
+
+  void QualifierTest() {
+    void (Qualifiers::*X)();
+    X = &Qualifiers::C; // expected-error {{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const': different qualifiers (none vs const)}}
+    X = &Qualifiers::V; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() volatile': different qualifiers (none vs volatile)}}
+    X = &Qualifiers::R; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() restrict': different qualifiers (none vs restrict)}}
+    X = &Qualifiers::CV; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const volatile': different qualifiers (none vs const and volatile)}}
+    X = &Qualifiers::CR; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const restrict': different qualifiers (none vs const and restrict)}}
+    X = &Qualifiers::VR; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() volatile restrict': different qualifiers (none vs volatile and restrict)}}
+    X = &Qualifiers::CVR; // expected-error{{assigning to 'void (test1::Qualifiers::*)()' from incompatible type 'void (test1::Qualifiers::*)() const volatile restrict': different qualifiers (none vs const, volatile, and restrict)}}
+  }
+
+  struct Dummy {
+    void N() {};
+  };
+
+  void (Qualifiers::*X)() = &Dummy::N; // expected-error{{cannot initialize a variable of type 'void (test1::Qualifiers::*)()' with an rvalue of type 'void (test1::Dummy::*)()': different classes ('test1::Qualifiers' vs 'test1::Dummy')}}
+}





More information about the cfe-commits mailing list