diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index e4df72ec1a377acd6000faf28f969b9eaed7ca04..80e6121ce440d27a6ae0d48dd2289b4edd2a6cff 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -455,8 +455,8 @@ struct rejection_reason {
       int expected;
       /* The actual number of arguments in the call.  */
       int actual;
-      /* Whether the call was a varargs call.  */
-      bool call_varargs_p;
+      /* Whether EXPECTED should be treated as a lower bound.  */
+      bool least_p;
     } arity;
     /* Information about an argument conversion mismatch.  */
     struct conversion_info conversion;
@@ -628,12 +628,13 @@ alloc_rejection (enum rejection_reason_code code)
 }
 
 static struct rejection_reason *
-arity_rejection (tree first_arg, int expected, int actual)
+arity_rejection (tree first_arg, int expected, int actual, bool least_p = false)
 {
   struct rejection_reason *r = alloc_rejection (rr_arity);
   int adjust = first_arg != NULL_TREE;
   r->u.arity.expected = expected - adjust;
   r->u.arity.actual = actual - adjust;
+  r->u.arity.least_p = least_p;
   return r;
 }
 
@@ -3452,6 +3453,44 @@ add_template_candidate_real (struct z_candidate **candidates, tree tmpl,
     }
   gcc_assert (ia == nargs_without_in_chrg);
 
+  if (!obj && explicit_targs)
+    {
+      /* Check that there's no obvious arity mismatch before proceeding with
+	 deduction.  This avoids substituting explicit template arguments
+	 into the template (which could result in an error outside the
+	 immediate context) when the resulting candidate would be unviable
+	 anyway.  */
+      int min_arity = 0, max_arity = 0;
+      tree parms = TYPE_ARG_TYPES (TREE_TYPE (tmpl));
+      parms = skip_artificial_parms_for (tmpl, parms);
+      for (; parms != void_list_node; parms = TREE_CHAIN (parms))
+	{
+	  if (!parms || PACK_EXPANSION_P (TREE_VALUE (parms)))
+	    {
+	      max_arity = -1;
+	      break;
+	    }
+	  if (TREE_PURPOSE (parms))
+	    /* A parameter with a default argument.  */
+	    ++max_arity;
+	  else
+	    ++min_arity, ++max_arity;
+	}
+      if (ia < (unsigned)min_arity)
+	{
+	  /* Too few arguments.  */
+	  reason = arity_rejection (NULL_TREE, min_arity, ia,
+				    /*least_p=*/(max_arity == -1));
+	  goto fail;
+	}
+      else if (max_arity != -1 && ia > (unsigned)max_arity)
+	{
+	  /* Too many arguments.  */
+	  reason = arity_rejection (NULL_TREE, max_arity, ia);
+	  goto fail;
+	}
+    }
+
   errs = errorcount+sorrycount;
   if (!obj)
     convs = alloc_conversions (nargs);
@@ -3725,12 +3764,19 @@ print_conversion_rejection (location_t loc, struct conversion_info *info,
    HAVE.  */
 
 static void
-print_arity_information (location_t loc, unsigned int have, unsigned int want)
-{
-  inform_n (loc, want,
-	    "  candidate expects %d argument, %d provided",
-	    "  candidate expects %d arguments, %d provided",
-	    want, have);
+print_arity_information (location_t loc, unsigned int have, unsigned int want,
+			 bool least_p)
+{
+  if (least_p)
+    inform_n (loc, want,
+	      "  candidate expects at least %d argument, %d provided",
+	      "  candidate expects at least %d arguments, %d provided",
+	      want, have);
+  else
+    inform_n (loc, want,
+	      "  candidate expects %d argument, %d provided",
+	      "  candidate expects %d arguments, %d provided",
+	      want, have);
 }
 
 /* Print information about one overload candidate CANDIDATE.  MSGSTR
@@ -3794,7 +3840,8 @@ print_z_candidate (location_t loc, const char *msgstr,
 	{
 	case rr_arity:
 	  print_arity_information (cloc, r->u.arity.actual,
-				   r->u.arity.expected);
+				   r->u.arity.expected,
+				   r->u.arity.least_p);
 	  break;
 	case rr_arg_conversion:
 	  print_conversion_rejection (cloc, &r->u.conversion, fn);
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype29.C b/gcc/testsuite/g++.dg/cpp0x/decltype29.C
index 51da8ddd0ded6a2c1049c6c720a6d21d2889173d..ea97b033569c03d6028983c9b8c1d3a71603253f 100644
--- a/gcc/testsuite/g++.dg/cpp0x/decltype29.C
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype29.C
@@ -10,10 +10,10 @@ ft() {}
 
 template<class F, int N>
 decltype (ft<F> (F()))		// { dg-error "depth" }
-ft() {}
+ft(F) {}
 
 int main() {
-    ft<struct a*, 0>();		// { dg-message "from here" }
+    ft<struct a*, 0>(0);		// { dg-message "from here" }
 }
 
 // { dg-prune-output "compilation terminated" }
diff --git a/gcc/testsuite/g++.dg/template/error56.C b/gcc/testsuite/g++.dg/template/error56.C
index e85471a50b09fcb0f3c4d616e034961de3b785f4..71206a1ae5cb7d998d7abaf521983be45e6b033a 100644
--- a/gcc/testsuite/g++.dg/template/error56.C
+++ b/gcc/testsuite/g++.dg/template/error56.C
@@ -3,12 +3,12 @@
 struct A
 {
   template <class T> void f(T);
-  void f();
+  void f(int);
 };
 
 int main()
 {
-  A().f<1>();			// { dg-error "f<1>" }
+  A().f<1>(0);			// { dg-error "f<1>" }
   // { dg-error "type/value mismatch at argument 1" "" { target *-*-* } .-1 }
   // { dg-message "expected a type, got .1." "" { target *-*-* } .-2 }
 }
diff --git a/gcc/testsuite/g++.dg/template/explicit-args7.C b/gcc/testsuite/g++.dg/template/explicit-args7.C
new file mode 100644
index 0000000000000000000000000000000000000000..fb5e89e4cf6074eee7388b5a6d6c49acb6baff22
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/explicit-args7.C
@@ -0,0 +1,33 @@
+// PR c++/12672
+// Verify we don't substitute explicit template arguments into
+// candidate function templates when the arity of the function
+// template disagrees with the arity of the call.
+
+template<class T>
+struct A { typedef typename T::type type; };
+
+template<class T> void f(T); // arity 1
+template<class T> void f(T, T, T); // arity 3
+
+template<class T> typename A<T>::type f(T, T); // arity 2
+template<class T, class U> typename A<T>::type f(U, U); // arity 2
+
+struct B {
+  template<class T> void f(T); // arity 1
+  template<class T> void f(T, T, T); // arity 3
+
+  template<class T> typename A<T>::type f(T, T); // arity 2
+  template<class T, class U> typename A<T>::type f(U, U); // arity 2
+};
+
+int main() {
+  // If overload resolution attempts deduction for any of the arity-2 function
+  // templates, the substitution of explicit arguments into the template would
+  // cause a hard error.
+  f<int>(1);
+  f<int>(1, 1, 1);
+
+  B b;
+  b.f<int>(1);
+  b.f<int>(1, 1, 1);
+}
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/unify6.C b/gcc/testsuite/g++.old-deja/g++.pt/unify6.C
index d122ec2dcb9c7599056a36d092a747e051e86f5e..ee14ceadd915d888f98dabc1181d4f3e38889f18 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/unify6.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/unify6.C
@@ -23,8 +23,8 @@ template<class T> void foo(T const *){} // { dg-error "pointer to reference" }
 
 void f()
 {
-  foo<int &>(); // { dg-error "" } attempt to build int & const *
-  foo<void ()>(); // { dg-error "" } attempt to build void (const *)()
+  foo<int &>(0); // { dg-error "" } attempt to build int & const *
+  foo<void ()>(0); // OK by [dcl.fct]/7, the const is silently dropped
 }
 
 typedef void (*Fptr)();