Patchwork C++ PATCH for DR 657 (abstract classes and sfinae)

login
register
mail settings
Submitter Jason Merrill
Date March 17, 2013, 1:33 a.m.
Message ID <51451D58.9050500@redhat.com>
Download mbox | patch
Permalink /patch/228256/
State New
Headers show

Comments

Jason Merrill - March 17, 2013, 1:33 a.m.
While I was looking at this and 17232 I also decided to improve the 
abstract class diagnostics.

Tested x86_64-pc-linux-gnu, applying to trunk.
Gabriel Dos Reis - March 17, 2013, 4:28 a.m.
On Sat, Mar 16, 2013 at 8:33 PM, Jason Merrill <jason@redhat.com> wrote:
> While I was looking at this and 17232 I also decided to improve the abstract
> class diagnostics.
>
> Tested x86_64-pc-linux-gnu, applying to trunk.

Now that we are writing C++, we can dispense with the
'typedef enum ....' stuff.

-- Gaby

Patch

commit 3f00e78856a76cf45d56d548ebc77755dc1d0333
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Feb 27 12:30:41 2013 -0500

    	* cp-tree.h (abstract_class_use): New enum.
    	* typeck2.c (pending_abstract_type): Add use field.
    	(abstract_virtuals_error_sfinae): Add overloads taking
    	abstract_class_use instead of tree.
    	* typeck.c (build_static_cast_1): Call it.
    	* except.c (is_admissible_throw_operand_or_catch_parameter): Call it.
    	* pt.c: Adjust calls.
    	* decl.c (cp_finish_decl): Don't handle functions specially.
    	(grokdeclarator): Always check return type.
    	* init.c (build_new_1): Adjust call.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e2a96a5..39fb3df 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -463,6 +463,19 @@  typedef enum impl_conv_void {
   ICV_THIRD_IN_FOR     /* for increment expression */
 } impl_conv_void;
 
+/* Possible invalid uses of an abstract class that might not have a
+   specific associated declaration.  */
+typedef enum abstract_class_use {
+  ACU_UNKNOWN,			/* unknown or decl provided */
+  ACU_CAST,			/* cast to abstract class */
+  ACU_NEW,			/* new-expression of abstract class */
+  ACU_THROW,			/* throw-expression of abstract class */
+  ACU_CATCH,			/* catch-parameter of abstract class */
+  ACU_ARRAY,			/* array of abstract class */
+  ACU_RETURN,			/* return type of abstract class */
+  ACU_PARM			/* parameter type of abstract class */
+} abstract_class_use;
+
 /* Macros for access to language-specific slots in an identifier.  */
 
 #define IDENTIFIER_NAMESPACE_BINDINGS(NODE)	\
@@ -5986,7 +5999,9 @@  extern tree binfo_or_else			(tree, tree);
 extern void cxx_readonly_error			(tree, enum lvalue_use);
 extern void complete_type_check_abstract	(tree);
 extern int abstract_virtuals_error		(tree, tree);
+extern int abstract_virtuals_error		(abstract_class_use, tree);
 extern int abstract_virtuals_error_sfinae	(tree, tree, tsubst_flags_t);
+extern int abstract_virtuals_error_sfinae	(abstract_class_use, tree, tsubst_flags_t);
 
 extern tree store_init_value			(tree, tree, vec<tree, va_gc>**, int);
 extern void check_narrowing			(tree, tree);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index bf34585..fed101f 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -6434,11 +6434,7 @@  cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
       /* Check for abstractness of the type. Notice that there is no
 	 need to strip array types here since the check for those types
 	 is already done within create_array_type_for_decl.  */
-      if (TREE_CODE (type) == FUNCTION_TYPE
-	  || TREE_CODE (type) == METHOD_TYPE)
-	abstract_virtuals_error (decl, TREE_TYPE (type));
-      else
-	abstract_virtuals_error (decl, type);
+      abstract_virtuals_error (decl, type);
 
       if (TREE_TYPE (decl) == error_mark_node)
 	/* No initialization required.  */
@@ -8656,6 +8652,7 @@  grokdeclarator (const cp_declarator *declarator,
   bool template_type_arg = false;
   bool template_parm_flag = false;
   bool constexpr_p = decl_spec_seq_has_spec_p (declspecs, ds_constexpr);
+  source_location saved_loc = input_location;
   const char *errmsg;
 
   signed_p = decl_spec_seq_has_spec_p (declspecs, ds_signed);
@@ -9340,7 +9337,6 @@  grokdeclarator (const cp_declarator *declarator,
   if (declspecs->std_attributes)
     {
       /* Apply the c++11 attributes to the type preceding them.  */
-      source_location saved_loc = input_location;
       input_location = declspecs->locations[ds_std_attribute];
       decl_attributes (&type, declspecs->std_attributes, 0);
       input_location = saved_loc;
@@ -9428,11 +9424,10 @@  grokdeclarator (const cp_declarator *declarator,
 		error ("%qs declared as function returning an array", name);
 		return error_mark_node;
 	      }
-	    /* When decl_context == NORMAL we emit a better error message
-	       later in abstract_virtuals_error.  */
-	    if (decl_context == TYPENAME && ABSTRACT_CLASS_TYPE_P (type))
-	      error ("%qs declared as function returning an abstract "
-		     "class type", name);
+
+	    input_location = declspecs->locations[ds_type_spec];
+	    abstract_virtuals_error (ACU_RETURN, type);
+	    input_location = saved_loc;
 
 	    /* Pick up type qualifiers which should be applied to `this'.  */
 	    memfn_quals = declarator->u.function.qualifiers;
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 216ec10..52ba1cd 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -972,16 +972,8 @@  is_admissible_throw_operand_or_catch_parameter (tree t, bool is_throw)
   /* 10.4/3 An abstract class shall not be used as a parameter type,
 	    as a function return type or as type of an explicit
 	    conversion.  */
-  else if (ABSTRACT_CLASS_TYPE_P (type))
-    {
-      if (is_throw)
-	error ("expression %qE of abstract class type %qT cannot "
-	       "be used in throw-expression", expr, type);
-      else
-	error ("cannot declare catch parameter to be of abstract "
-	       "class type %qT", type);
-      return false;
-    }
+  else if (abstract_virtuals_error (is_throw ? ACU_THROW : ACU_CATCH, type))
+    return false;
   else if (!is_throw
 	   && TREE_CODE (type) == REFERENCE_TYPE
 	   && TYPE_REF_IS_RVALUE (type))
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 697f11f..679c47d 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2301,7 +2301,7 @@  build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
       return error_mark_node;
     }
 
-  if (abstract_virtuals_error_sfinae (NULL_TREE, elt_type, complain))
+  if (abstract_virtuals_error_sfinae (ACU_NEW, elt_type, complain))
     return error_mark_node;
 
   is_initialized = (type_build_ctor_call (elt_type) || *init != NULL);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index fd1ad9b..4ffc353 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -10851,7 +10851,7 @@  tsubst_arg_types (tree arg_types,
         return error_mark_node;
     }
     /* DR 657. */
-    if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain))
+    if (abstract_virtuals_error_sfinae (ACU_PARM, type, complain))
       return error_mark_node;
     
     /* Do array-to-pointer, function-to-pointer conversion, and ignore
@@ -10930,7 +10930,7 @@  tsubst_function_type (tree t,
       return error_mark_node;
     }
   /* And DR 657. */
-  if (abstract_virtuals_error_sfinae (NULL_TREE, return_type, complain))
+  if (abstract_virtuals_error_sfinae (ACU_RETURN, return_type, complain))
     return error_mark_node;
 
   /* Substitute the argument types.  */
@@ -11654,7 +11654,7 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	    return error_mark_node;
 	  }
 
-	if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain))
+	if (abstract_virtuals_error_sfinae (ACU_ARRAY, type, complain))
 	  return error_mark_node;
 
 	r = build_cplus_array_type (type, domain);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 58ebcc0..7fa5304 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -6238,6 +6238,12 @@  build_static_cast_1 (tree type, tree expr, bool c_cast_p,
   if (TREE_CODE (type) == VOID_TYPE)
     return convert_to_void (expr, ICV_CAST, complain);
 
+  /* [class.abstract]
+     An abstract class shall not be used ... as the type of an explicit
+     conversion.  */
+  if (abstract_virtuals_error_sfinae (ACU_CAST, type, complain))
+    return error_mark_node;
+
   /* [expr.static.cast]
 
      An expression e can be explicitly converted to a type T using a
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 37c42e2..6ef46a1 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -141,6 +141,9 @@  struct GTY((chain_next ("%h.next"))) pending_abstract_type {
   /* Type which will be checked for abstractness.  */
   tree type;
 
+  /* Kind of use in an unnamed declarator.  */
+  abstract_class_use use;
+
   /* Position of the declaration. This is only needed for IDENTIFIER_NODEs,
      because DECLs already carry locus information.  */
   location_t locus;
@@ -181,6 +184,7 @@  pat_compare (const void* val1, const void* val2)
 static GTY ((param_is (struct pending_abstract_type)))
 htab_t abstract_pending_vars = NULL;
 
+static int abstract_virtuals_error_sfinae (tree, tree, abstract_class_use, tsubst_flags_t);
 
 /* This function is called after TYPE is completed, and will check if there
    are pending declarations for which we still need to verify the abstractness
@@ -231,7 +235,8 @@  complete_type_check_abstract (tree type)
 	    location. Notice that this is only needed if the decl is an
 	    IDENTIFIER_NODE.  */
 	  input_location = pat->locus;
-	  abstract_virtuals_error (pat->decl, pat->type);
+	  abstract_virtuals_error_sfinae (pat->decl, pat->type, pat->use,
+					  tf_warning_or_error);
 	  pat = pat->next;
 	}
     }
@@ -244,11 +249,13 @@  complete_type_check_abstract (tree type)
 
 /* If TYPE has abstract virtual functions, issue an error about trying
    to create an object of that type.  DECL is the object declared, or
-   NULL_TREE if the declaration is unavailable.  Returns 1 if an error
-   occurred; zero if all was well.  */
+   NULL_TREE if the declaration is unavailable, in which case USE specifies
+   the kind of invalid use.  Returns 1 if an error occurred; zero if
+   all was well.  */
 
-int
-abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
+static int
+abstract_virtuals_error_sfinae (tree decl, tree type, abstract_class_use use,
+				tsubst_flags_t complain)
 {
   vec<tree, va_gc> *pure;
 
@@ -284,6 +291,7 @@  abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
       pat = ggc_alloc_pending_abstract_type ();
       pat->type = type;
       pat->decl = decl;
+      pat->use = use;
       pat->locus = ((decl && DECL_P (decl))
 		    ? DECL_SOURCE_LOCATION (decl)
 		    : input_location);
@@ -312,8 +320,14 @@  abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
 	error ("cannot declare variable %q+D to be of abstract "
 	       "type %qT", decl, type);
       else if (TREE_CODE (decl) == PARM_DECL)
-	error ("cannot declare parameter %q+D to be of abstract type %qT",
-	       decl, type);
+	{
+	  if (DECL_NAME (decl))
+	    error ("cannot declare parameter %q+D to be of abstract type %qT",
+		   decl, type);
+	  else
+	    error ("cannot declare parameter to be of abstract type %qT",
+		   type);
+	}
       else if (TREE_CODE (decl) == FIELD_DECL)
 	error ("cannot declare field %q+D to be of abstract type %qT",
 	       decl, type);
@@ -328,8 +342,34 @@  abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
       else
 	error ("invalid abstract type for %q+D", decl);
     }
-  else
-    error ("cannot allocate an object of abstract type %qT", type);
+  else switch (use)
+    {
+    case ACU_ARRAY:
+      error ("creating array of %qT, which is an abstract class type", type);
+      break;
+    case ACU_CAST:
+      error ("invalid cast to abstract class type %qT", type);
+      break;
+    case ACU_NEW:
+      error ("invalid new-expression of abstract class type %qT", type);
+      break;
+    case ACU_RETURN:
+      error ("invalid abstract return type %qT", type);
+      break;
+    case ACU_PARM:
+      error ("invalid abstract parameter type %qT", type);
+      break;
+    case ACU_THROW:
+      error ("expression of abstract class type %qT cannot "
+	     "be used in throw-expression", type);
+      break;
+    case ACU_CATCH:
+      error ("cannot declare catch parameter to be of abstract "
+	     "class type %qT", type);
+      break;
+    default:
+      error ("cannot allocate an object of abstract type %qT", type);
+    }
 
   /* Only go through this once.  */
   if (pure->length ())
@@ -351,14 +391,24 @@  abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
 	 again.  */
       pure->truncate (0);
     }
-  else
-    inform (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (type)),
-	    "  since type %qT has pure virtual functions",
-	    type);
 
   return 1;
 }
 
+int
+abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
+{
+  return abstract_virtuals_error_sfinae (decl, type, ACU_UNKNOWN, complain);
+}
+
+int
+abstract_virtuals_error_sfinae (abstract_class_use use, tree type,
+				tsubst_flags_t complain)
+{
+  return abstract_virtuals_error_sfinae (NULL_TREE, type, use, complain);
+}
+
+
 /* Wrapper for the above function in the common case of wanting errors.  */
 
 int
@@ -367,6 +417,12 @@  abstract_virtuals_error (tree decl, tree type)
   return abstract_virtuals_error_sfinae (decl, type, tf_warning_or_error);
 }
 
+int
+abstract_virtuals_error (abstract_class_use use, tree type)
+{
+  return abstract_virtuals_error_sfinae (use, type, tf_warning_or_error);
+}
+
 /* Print an error message for invalid use of an incomplete type.
    VALUE is the expression that was used (or 0 if that isn't known)
    and TYPE is the type that was invalid.  DIAG_KIND indicates the
@@ -1733,7 +1789,7 @@  build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
 
   if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
     return error_mark_node;
-  if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain))
+  if (abstract_virtuals_error_sfinae (ACU_CAST, type, complain))
     return error_mark_node;
 
   /* [expr.type.conv]
diff --git a/gcc/testsuite/g++.dg/other/abstract5.C b/gcc/testsuite/g++.dg/other/abstract5.C
new file mode 100644
index 0000000..d13dd9e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/other/abstract5.C
@@ -0,0 +1,6 @@ 
+struct A
+{
+  virtual void f() = 0;
+};
+
+typedef A (*fp)();		// { dg-error "abstract" }