]> granicus.if.org Git - clang/commitdiff
When overload resolution fails for an overloaded operator, show the
authorDouglas Gregor <dgregor@apple.com>
Wed, 30 Sep 2009 21:46:01 +0000 (21:46 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 30 Sep 2009 21:46:01 +0000 (21:46 +0000)
overload candidates (but not the built-in ones). We still rely on the
underlying built-in semantic analysis to produce the initial
diagnostic, then print the candidates following that diagnostic.

One side advantage of this approach is that we can perform more validation
of C++'s operator overloading with built-in candidates vs. the
semantic analysis for those built-in operators: when there are no
viable candidates, we know to expect an error from the built-in
operator handling code. Otherwise, we are not modeling the built-in
semantics properly within operator overloading. This is checked as:

      assert(Result.isInvalid() &&
             "C++ binary operator overloading is missing
             candidates!");
      if (Result.isInvalid())
        PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);

The assert() catches cases where we're wrong in a +Asserts build. The
"if" makes sure that, if this happens in a production clang
(-Asserts), we still build the proper built-in operator and continue
on our merry way. This is effectively what happened before this
change, but we've added the assert() to catch more flies.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@83175 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
test/SemaCXX/copy-assignment.cpp
test/SemaCXX/namespace.cpp
test/SemaCXX/overloaded-builtin-operators.cpp
test/SemaCXX/overloaded-operator.cpp
test/SemaTemplate/qualified-names-diag.cpp

index 2f653b53202f1552f918056f64dfbd8501e62137..414525d6c3322fdf8317d6f9354d2415a58e2495 100644 (file)
@@ -1599,11 +1599,19 @@ Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
       }
     }
 
-    case OR_No_Viable_Function:
-      // No viable function; fall through to handling this as a
-      // built-in operator, which will produce an error message for us.
-      break;
-
+    case OR_No_Viable_Function: {
+      // No viable function; try checking this as a built-in operator, which
+      // will fail and provide a diagnostic. Then, print the overload
+      // candidates.
+      OwningExprResult Result = CreateBuiltinUnaryOp(OpLoc, Opc, move(Input));
+      assert(Result.isInvalid() && 
+             "C++ postfix-unary operator overloading is missing candidates!");
+      if (Result.isInvalid())
+        PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
+      
+      return move(Result);
+    }
+        
     case OR_Ambiguous:
       Diag(OpLoc,  diag::err_ovl_ambiguous_oper)
           << UnaryOperator::getOpcodeStr(Opc)
index aaf76b1a0ec1132263f7c976659c83431d9d123c..6966926e9e920c034908459dda5e223352d049f1 100644 (file)
@@ -3998,10 +3998,11 @@ Sema::PrintOverloadCandidates(OverloadCandidateSet& CandidateSet,
 
         Diag(Cand->Surrogate->getLocation(), diag::err_ovl_surrogate_cand)
           << FnType;
-      } else {
+      } else if (OnlyViable) {
         // FIXME: We need to get the identifier in here
         // FIXME: Do we want the error message to point at the operator?
         // (built-ins won't have a location)
+        // FIXME: can we get some kind of stable location for this?
         QualType FnType
           = Context.getFunctionType(Cand->BuiltinTypes.ResultTy,
                                     Cand->BuiltinTypes.ParamTypes,
@@ -4631,19 +4632,34 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
       }
     }
 
-    case OR_No_Viable_Function:
+    case OR_No_Viable_Function: {
+      // C++ [over.match.oper]p9:
+      //   If the operator is the operator , [...] and there are no
+      //   viable functions, then the operator is assumed to be the
+      //   built-in operator and interpreted according to clause 5.
+      if (Opc == BinaryOperator::Comma)
+        break;
+
       // For class as left operand for assignment or compound assigment operator
       // do not fall through to handling in built-in, but report that no overloaded
       // assignment operator found
-      if (Args[0]->getType()->isRecordType() && Opc >= BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign) {
+      OwningExprResult Result = ExprError();
+      if (Args[0]->getType()->isRecordType() && 
+          Opc >= BinaryOperator::Assign && Opc <= BinaryOperator::OrAssign) {
         Diag(OpLoc,  diag::err_ovl_no_viable_oper)
              << BinaryOperator::getOpcodeStr(Opc)
              << Args[0]->getSourceRange() << Args[1]->getSourceRange();
-        return ExprError();
+      } else {
+        // No viable function; try to create a built-in operation, which will
+        // produce an error. Then, show the non-viable candidates.
+        Result = CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
       }
-      // No viable function; fall through to handling this as a
-      // built-in operator, which will produce an error message for us.
-      break;
+      assert(Result.isInvalid() && 
+             "C++ binary operator overloading is missing candidates!");
+      if (Result.isInvalid())
+        PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
+      return move(Result);
+    }
 
     case OR_Ambiguous:
       Diag(OpLoc,  diag::err_ovl_ambiguous_oper)
@@ -4661,9 +4677,7 @@ Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
       return ExprError();
     }
 
-  // Either we found no viable overloaded operator or we matched a
-  // built-in operator. In either case, try to build a built-in
-  // operation.
+  // We matched a built-in operator; build it.
   return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
 }
 
index 6e5012f5a7a130a8db7912f71b310c3542f2a38f..413e4d1933676ae7ad7e32da3b9282ddcdf8e54f 100644 (file)
@@ -11,7 +11,7 @@ struct ConvertibleToConstA {
 };
 
 struct B {
-  B& operator=(B&);
+  B& operator=(B&);  // expected-note 4 {{candidate function}}
 };
 
 struct ConvertibleToB {
index 696ea818f6570eea24080fbd0e74eb27a39d66e1..ae8dfc5813661880b29b2a52adaa1d4f249f70fb 100644 (file)
@@ -8,7 +8,8 @@ void f() { A = 0; } // expected-error {{unexpected namespace name 'A': expected
 int A; // expected-error {{redefinition of 'A' as different kind of symbol}}
 class A; // expected-error {{redefinition of 'A' as different kind of symbol}}
 
-class B {}; // expected-note {{previous definition is here}}
+class B {}; // expected-note {{previous definition is here}} \
+            // FIXME: ugly expected-note{{candidate function}}
 
 void C(); // expected-note {{previous definition is here}}
 namespace C {} // expected-error {{redefinition of 'C' as different kind of symbol}}
index a8c94f182225cd76e74e6e93766d0061ccfe6151..0284b2929b3c0175d14f870e53299e155f718253 100644 (file)
@@ -59,7 +59,7 @@ void f(Short s, Long l, Enum1 e1, Enum2 e2, Xpmf pmf) {
   // FIXME: should pass (void)static_cast<no&>(islong(e1 % e2));
 }
 
-struct ShortRef {
+struct ShortRef { // expected-note{{candidate function}}
   operator short&();
 };
 
@@ -67,7 +67,7 @@ struct LongRef {
   operator volatile long&();
 };
 
-struct XpmfRef {
+struct XpmfRef { // expected-note{{candidate function}}
   operator pmf&();
 };
 
index 8471f3c936b7007ec0b7802d4ca2de2c4e3da32f..8f71ad53813822226e50d549b8b913ff7ac89ddb 100644 (file)
@@ -28,12 +28,12 @@ void g(Y y, Z z) {
 }
 
 struct A {
-  bool operator==(Z&); // expected-note{{candidate function}}
+  bool operator==(Z&); // expected-note 2{{candidate function}}
 };
 
 A make_A();
 
-bool operator==(A&, Z&); // expected-note{{candidate function}}
+bool operator==(A&, Z&); // expected-note 2{{candidate function}}
 
 void h(A a, const A ac, Z z) {
   make_A() == z;
index c875332905fb321fbd9abba14e15947ed7b0f7bf..1d53e5cb98ba3d29cb9f1792cec4269ac3dfc958 100644 (file)
@@ -1,7 +1,7 @@
 // RUN: clang-cc -fsyntax-only -verify %s
 
 namespace std {
-  template<typename T> class vector { };
+  template<typename T> class vector { }; // expected-note{{candidate}}
 }
 
 typedef int INT;