[C++ PATCH 1/3] Fix various latent issues revealed by P0732 work.

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

[C++ PATCH 1/3] Fix various latent issues revealed by P0732 work.

Jason Merrill
The initialized_type hunk fixes handling of void AGGR_INIT_EXPRs that call a
non-constructor; an AGGR_INIT_EXPR can have void type if its initialization
semantics are more complicated than just expanding the call.

The cxx_eval_vec_init_1 hunk corrects AGGR_INIT_EXPRs that were
nonsensically built to initialize an object of void type.  And the
build_aggr_init_expr hunk makes sure we don't do that again.

The ocp_convert and cxx_eval_outermost_constant_expr hunks deal with making
sure that a constant CONSTRUCTOR has the right type.

Tested x86_64-pc-linux-gnu, applying to trunk.

        * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
        * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
        (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
        (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
        right type.  Don't wrap a CONSTRUCTOR if one was passed in.
        * tree.c (build_aggr_init_expr): Check for void.
---
 gcc/cp/constexpr.c | 22 +++++++++++++---------
 gcc/cp/cvt.c       | 10 +++++++++-
 gcc/cp/tree.c      |  2 ++
 gcc/cp/ChangeLog   |  7 +++++++
 4 files changed, 31 insertions(+), 10 deletions(-)

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 7692b1727da..4fb1ba527e3 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2778,8 +2778,10 @@ initialized_type (tree t)
 {
   if (TYPE_P (t))
     return t;
-  tree type = cv_unqualified (TREE_TYPE (t));
-  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
+  tree type = TREE_TYPE (t);
+  if (!VOID_TYPE_P (type))
+    /* No need to look deeper.  */;
+  else if (TREE_CODE (t) == CALL_EXPR)
     {
       /* A constructor call has void type, so we need to look deeper.  */
       tree fn = get_function_named_in_call (t);
@@ -2787,7 +2789,9 @@ initialized_type (tree t)
   && DECL_CXX_CONSTRUCTOR_P (fn))
  type = DECL_CONTEXT (fn);
     }
-  return type;
+  else if (TREE_CODE (t) == AGGR_INIT_EXPR)
+    type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
+  return cv_unqualified (type);
 }
 
 /* We're about to initialize element INDEX of an array or class from VALUE.
@@ -3000,7 +3004,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
  &argvec, elttype, LOOKUP_NORMAL,
  complain);
       release_tree_vector (argvec);
-      init = build_aggr_init_expr (TREE_TYPE (init), init);
+      init = build_aggr_init_expr (elttype, init);
       pre_init = true;
     }
 
@@ -5089,7 +5093,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
  r = build_nop (TREE_TYPE (r), r);
       TREE_CONSTANT (r) = false;
     }
-  else if (non_constant_p || r == t)
+  else if (non_constant_p)
     return t;
 
   if (should_unshare)
@@ -5097,18 +5101,18 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
 
   if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
     {
+      r = adjust_temp_type (type, r);
       if (TREE_CODE (t) == TARGET_EXPR
   && TARGET_EXPR_INITIAL (t) == r)
  return t;
-      else
+      else if (TREE_CODE (t) != CONSTRUCTOR)
  {
   r = get_target_expr (r);
   TREE_CONSTANT (r) = true;
-  return r;
  }
     }
-  else
-    return r;
+
+  return r;
 }
 
 /* Returns true if T is a valid subexpression of a constant expression,
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 315b0d6a65a..b04e9a70652 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -725,7 +725,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
     /* We need a new temporary; don't take this shortcut.  */;
   else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
     {
-      if (same_type_p (type, TREE_TYPE (e)))
+      tree etype = TREE_TYPE (e);
+      if (same_type_p (type, etype))
  /* The call to fold will not always remove the NOP_EXPR as
    might be expected, since if one of the types is a typedef;
    the comparison in fold is just equality of pointers, not a
@@ -743,9 +744,16 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
  {
   /* Don't build a NOP_EXPR of class type.  Instead, change the
      type of the temporary.  */
+  gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
   TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
   return e;
  }
+      else if (TREE_CODE (e) == CONSTRUCTOR)
+ {
+  gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
+  TREE_TYPE (e) = type;
+  return e;
+ }
       else
  {
   /* We shouldn't be treating objects of ADDRESSABLE type as
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 74018e97bb7..51af9f2015e 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -576,6 +576,8 @@ build_aggr_init_expr (tree type, tree init)
   tree rval;
   int is_ctor;
 
+  gcc_assert (!VOID_TYPE_P (type));
+
   /* Don't build AGGR_INIT_EXPR in a template.  */
   if (processing_template_decl)
     return init;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 7e9c0e2642a..4f40627a226 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,12 @@
 2018-11-04  Jason Merrill  <[hidden email]>
 
+ * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
+ * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
+ (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
+ (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
+ right type.  Don't wrap a CONSTRUCTOR if one was passed in.
+ * tree.c (build_aggr_init_expr): Check for void.
+
  PR c++/60503 - wrong lambda attribute syntax.
  * parser.c (cp_parser_lambda_declarator_opt): Fix attribute
  handling.

base-commit: 703d2f69fad4772dc4aedb5327b5e3d88e8e1843
--
2.17.2

Reply | Threaded
Open this post in threaded view
|

[PATCH 2/3] Implement P0732R2, class types in non-type template parameters.

Jason Merrill
There is one significant piece of this that is not implemented yet: the
reliance on defaulted operator<=>, which someone else has been working on.
So, for the moment those lines are commented out of the testcases.

One tricky bit was treating template parameters of classtype as const
lvalues without making their decltype const; for this I used a
VIEW_CONVERT_EXPR wrapper, which previously could only appear in templates
as location wrappers.

The user-defined literal parts of P0732R2 are in the next patch.

gcc/cp/
        * error.c (dump_simple_decl): Look through a template parm object.
        * mangle.c (write_template_arg): Likewise.
        (mangle_template_parm_object): New.
        * pt.c (template_parm_object_p, get_template_parm_object): New.
        (invalid_tparm_referent_p): Factor from convert_nontype_argument.
        (convert_nontype_argument, invalid_nontype_parm_type_p): Handle
        class-type template arguments.
        * tree.c (lvalue_kind): Likewise.
gcc/c-family/
        * c-cppbuiltin.c (c_cpp_builtins): Add
        __cpp_nontype_template_parameter_class.
libiberty/
        * cp-demangle.c (d_dump, d_make_comp, d_count_templates_scopes)
        (d_print_comp_inner): Handle DEMANGLE_COMPONENT_TPARM_OBJ.
        (d_special_name): Handle TA.
        (d_expresion_1): Fix demangling of brace-enclosed initializer list.
include/
        * demangle.h (enum demangle_component_type): Add
        DEMANGLE_COMPONENT_TPARM_OBJ.
---
 gcc/cp/cp-tree.h                            |   2 +
 include/demangle.h                          |   3 +
 gcc/c-family/c-cppbuiltin.c                 |   1 +
 gcc/cp/error.c                              |   3 +
 gcc/cp/mangle.c                             |  17 +
 gcc/cp/pt.c                                 | 352 +++++++++++++-------
 gcc/cp/semantics.c                          |  16 +-
 gcc/cp/tree.c                               |   6 +
 libiberty/cp-demangle.c                     |  17 +-
 gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C     |   6 +
 gcc/testsuite/g++.dg/cpp2a/nontype-class1.C |  33 ++
 gcc/testsuite/g++.dg/cpp2a/nontype-class2.C |  17 +
 gcc/testsuite/g++.dg/cpp2a/nontype-class3.C |  16 +
 gcc/testsuite/g++.dg/cpp2a/nontype-class4.C |  18 +
 gcc/testsuite/g++.dg/cpp2a/nontype-class5.C |  12 +
 gcc/testsuite/g++.dg/cpp2a/nontype-class7.C |  26 ++
 gcc/testsuite/g++.dg/cpp2a/nontype-class8.C |  26 ++
 gcc/testsuite/g++.dg/template/crash55.C     |   2 +-
 gcc/testsuite/g++.dg/template/nontype16.C   |   2 +-
 gcc/testsuite/g++.dg/template/nontype4.C    |   2 +-
 gcc/testsuite/g++.dg/template/nontype5.C    |   2 +-
 gcc/testsuite/g++.dg/template/operator10.C  |   2 +-
 gcc/c-family/ChangeLog                      |   5 +
 gcc/cp/ChangeLog                            |  10 +
 include/ChangeLog                           |   5 +
 libiberty/ChangeLog                         |   8 +
 libiberty/testsuite/demangle-expected       |   6 +
 27 files changed, 494 insertions(+), 121 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class7.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class8.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6d49744b830..61b431e5f9d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6746,6 +6746,7 @@ extern bool variable_template_specialization_p  (tree);
 extern bool alias_type_or_template_p            (tree);
 extern bool alias_template_specialization_p     (const_tree);
 extern bool dependent_alias_template_spec_p     (const_tree);
+extern bool template_parm_object_p (const_tree);
 extern bool explicit_class_specialization_p     (tree);
 extern bool push_tinst_level                    (tree);
 extern bool push_tinst_level_loc                (tree, location_t);
@@ -7446,6 +7447,7 @@ extern tree mangle_tls_init_fn (tree);
 extern tree mangle_tls_wrapper_fn (tree);
 extern bool decl_tls_wrapper_p (tree);
 extern tree mangle_ref_init_variable (tree);
+extern tree mangle_template_parm_object (tree);
 extern char * get_mangled_vtable_map_var_name   (tree);
 extern bool mangle_return_type_p (tree);
 extern tree mangle_decomp (tree, vec<tree> &);
diff --git a/include/demangle.h b/include/demangle.h
index b8d57cf2951..4f920f2b7f5 100644
--- a/include/demangle.h
+++ b/include/demangle.h
@@ -392,6 +392,9 @@ enum demangle_component_type
      template argument, and the right subtree is either NULL or
      another TEMPLATE_ARGLIST node.  */
   DEMANGLE_COMPONENT_TEMPLATE_ARGLIST,
+  /* A template parameter object (C++20).  The left subtree is the
+     corresponding template argument.  */
+  DEMANGLE_COMPONENT_TPARM_OBJ,
   /* An initializer list.  The left subtree is either an explicit type or
      NULL, and the right subtree is a DEMANGLE_COMPONENT_ARGLIST.  */
   DEMANGLE_COMPONENT_INITIALIZER_LIST,
diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index b085cf9201f..e7f4c669056 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -979,6 +979,7 @@ c_cpp_builtins (cpp_reader *pfile)
  {
   /* Set feature test macros for C++2a.  */
   cpp_define (pfile, "__cpp_explicit_bool=201806");
+  cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
  }
       if (flag_concepts)
  cpp_define (pfile, "__cpp_concepts=201507");
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index fa115aa211c..72b42bd5cbe 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -1006,6 +1006,9 @@ dump_global_iord (cxx_pretty_printer *pp, tree t)
 static void
 dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags)
 {
+  if (template_parm_object_p (t))
+    return dump_expr (pp, DECL_INITIAL (t), flags);
+
   if (flags & TFF_DECL_SPECIFIERS)
     {
       if (VAR_P (t) && DECL_DECLARED_CONSTEXPR_P (t))
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 690d0bbdbd3..1b323015ded 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -3437,6 +3437,10 @@ write_template_arg (tree node)
  }
     }
 
+  if (template_parm_object_p (node))
+    /* We want to mangle the argument, not the var we stored it in.  */
+    node = DECL_INITIAL (node);
+
   /* Strip a conversion added by convert_nontype_argument.  */
   if (TREE_CODE (node) == IMPLICIT_CONV_EXPR)
     node = TREE_OPERAND (node, 0);
@@ -4257,6 +4261,19 @@ mangle_ref_init_variable (const tree variable)
   write_unsigned_number (temp_count++);
   return finish_mangling_get_identifier ();
 }
+
+/* Return an identifier for the mangled name of a C++20 template parameter
+   object for template argument EXPR.  */
+
+tree
+mangle_template_parm_object (tree expr)
+{
+  start_mangling (expr);
+  write_string ("_ZTAX");
+  write_expression (expr);
+  write_char ('E');
+  return finish_mangling_get_identifier ();
+}
 
 /* Given a CLASS_TYPE, such as a record for std::bad_exception this
    function generates a mangled name for the vtable map variable of
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 4226d4d6b5c..80b4eec8fdb 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6584,6 +6584,169 @@ unify_template_argument_mismatch (bool explain_p, tree parm, tree arg)
   return unify_invalid (explain_p);
 }
 
+/* True if T is a C++20 template parameter object to store the argument for a
+   template parameter of class type.  */
+
+bool
+template_parm_object_p (const_tree t)
+{
+  return (TREE_CODE (t) == VAR_DECL && DECL_ARTIFICIAL (t) && DECL_NAME (t)
+  && !strncmp (IDENTIFIER_POINTER (DECL_NAME (t)), "_ZTA", 4));
+}
+
+/* Subroutine of convert_nontype_argument, to check whether EXPR, as an
+   argument for TYPE, points to an unsuitable object.  */
+
+static bool
+invalid_tparm_referent_p (tree type, tree expr, tsubst_flags_t complain)
+{
+  switch (TREE_CODE (expr))
+    {
+    CASE_CONVERT:
+      return invalid_tparm_referent_p (type, TREE_OPERAND (expr, 0),
+       complain);
+
+    case TARGET_EXPR:
+      return invalid_tparm_referent_p (type, TARGET_EXPR_INITIAL (expr),
+       complain);
+
+    case CONSTRUCTOR:
+      {
+ unsigned i; tree elt;
+ FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (expr), i, elt)
+  if (invalid_tparm_referent_p (TREE_TYPE (elt), elt, complain))
+    return true;
+      }
+      break;
+
+    case ADDR_EXPR:
+      {
+ tree decl = TREE_OPERAND (expr, 0);
+
+ if (!VAR_P (decl))
+  {
+    if (complain & tf_error)
+      error ("%qE is not a valid template argument of type %qT "
+     "because %qE is not a variable", expr, type, decl);
+    return true;
+  }
+ else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl))
+  {
+    if (complain & tf_error)
+      error ("%qE is not a valid template argument of type %qT "
+     "in C++98 because %qD does not have external linkage",
+     expr, type, decl);
+    return true;
+  }
+ else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx17)
+ && decl_linkage (decl) == lk_none)
+  {
+    if (complain & tf_error)
+      error ("%qE is not a valid template argument of type %qT "
+     "because %qD has no linkage", expr, type, decl);
+    return true;
+  }
+ /* C++17: For a non-type template-parameter of reference or pointer
+   type, the value of the constant expression shall not refer to (or
+   for a pointer type, shall not be the address of):
+   * a subobject (4.5),
+   * a temporary object (15.2),
+   * a string literal (5.13.5),
+   * the result of a typeid expression (8.2.8), or
+   * a predefined __func__ variable (11.4.1).  */
+ else if (DECL_ARTIFICIAL (decl))
+  {
+    if (complain & tf_error)
+      error ("the address of %qD is not a valid template argument",
+     decl);
+    return true;
+  }
+ else if (!same_type_ignoring_top_level_qualifiers_p
+ (strip_array_types (TREE_TYPE (type)),
+  strip_array_types (TREE_TYPE (decl))))
+  {
+    if (complain & tf_error)
+      error ("the address of the %qT subobject of %qD is not a "
+     "valid template argument", TREE_TYPE (type), decl);
+    return true;
+  }
+ else if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
+  {
+    if (complain & tf_error)
+      error ("the address of %qD is not a valid template argument "
+     "because it does not have static storage duration",
+     decl);
+    return true;
+  }
+      }
+      break;
+
+    default:
+      if (!INDIRECT_TYPE_P (type))
+ /* We're only concerned about pointers and references here.  */;
+      else if (cxx_dialect >= cxx11 && integer_zerop (expr))
+ /* Null pointer values are OK in C++11.  */;
+      else
+ {
+  if (VAR_P (expr))
+    {
+      if (complain & tf_error)
+ error ("%qD is not a valid template argument "
+       "because %qD is a variable, not the address of "
+       "a variable", expr, expr);
+      return true;
+    }
+  else
+    {
+      if (complain & tf_error)
+ error ("%qE is not a valid template argument for %qT "
+       "because it is not the address of a variable",
+       expr, type);
+      return true;
+    }
+ }
+    }
+  return false;
+
+}
+
+/* Return a VAR_DECL for the C++20 template parameter object corresponding to
+   template argument EXPR.  */
+
+static tree
+get_template_parm_object (tree expr, tsubst_flags_t complain)
+{
+  if (TREE_CODE (expr) == TARGET_EXPR)
+    expr = TARGET_EXPR_INITIAL (expr);
+
+  if (!TREE_CONSTANT (expr))
+    {
+      if ((complain & tf_error)
+  && require_rvalue_constant_expression (expr))
+ cxx_constant_value (expr);
+      return error_mark_node;
+    }
+  if (invalid_tparm_referent_p (TREE_TYPE (expr), expr, complain))
+    return error_mark_node;
+
+  tree name = mangle_template_parm_object (expr);
+  tree decl = get_global_binding (name);
+  if (decl)
+    return decl;
+
+  tree type = cp_build_qualified_type (TREE_TYPE (expr), TYPE_QUAL_CONST);
+  decl = create_temporary_var (type);
+  TREE_STATIC (decl) = true;
+  DECL_DECLARED_CONSTEXPR_P (decl) = true;
+  TREE_READONLY (decl) = true;
+  DECL_NAME (decl) = name;
+  SET_DECL_ASSEMBLER_NAME (decl, name);
+  DECL_CONTEXT (decl) = global_namespace;
+  comdat_linkage (decl);
+  pushdecl_top_level_and_finish (decl, expr);
+  return decl;
+}
+
 /* Attempt to convert the non-type template parameter EXPR to the
    indicated TYPE.  If the conversion is successful, return the
    converted value.  If the conversion is unsuccessful, return
@@ -6609,15 +6772,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 {
   tree expr_type;
   location_t loc = cp_expr_loc_or_loc (expr, input_location);
-  tree orig_expr = expr;
 
   /* Detect immediately string literals as invalid non-type argument.
      This special-case is not needed for correctness (we would easily
      catch this later), but only to provide better diagnostic for this
      common user mistake. As suggested by DR 100, we do not mention
      linkage issues in the diagnostic as this is not the point.  */
-  /* FIXME we're making this OK.  */
-  if (TREE_CODE (expr) == STRING_CST)
+  if (TREE_CODE (expr) == STRING_CST && !CLASS_TYPE_P (type))
     {
       if (complain & tf_error)
  error ("%qE is not a valid template argument for type %qT "
@@ -6841,88 +7002,13 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
  ;
       else if (cxx_dialect >= cxx11 && integer_zerop (expr))
  /* Null pointer values are OK in C++11.  */;
-      else if (TREE_CODE (expr) != ADDR_EXPR)
- {
-  if (VAR_P (expr))
-    {
-      if (complain & tf_error)
- error ("%qD is not a valid template argument "
-       "because %qD is a variable, not the address of "
-       "a variable", orig_expr, expr);
-      return NULL_TREE;
-    }
-  if (INDIRECT_TYPE_P (expr_type))
-    {
-      if (complain & tf_error)
- error ("%qE is not a valid template argument for %qT "
-       "because it is not the address of a variable",
-       orig_expr, type);
-      return NULL_TREE;
-    }
-  /* Other values, like integer constants, might be valid
-     non-type arguments of some other type.  */
-  return error_mark_node;
- }
-      else
- {
-  tree decl = TREE_OPERAND (expr, 0);
-
-  if (!VAR_P (decl))
-    {
-      if (complain & tf_error)
- error ("%qE is not a valid template argument of type %qT "
-       "because %qE is not a variable", orig_expr, type, decl);
-      return NULL_TREE;
-    }
-  else if (cxx_dialect < cxx11 && !DECL_EXTERNAL_LINKAGE_P (decl))
-    {
-      if (complain & tf_error)
- error ("%qE is not a valid template argument of type %qT "
-       "because %qD does not have external linkage",
-       orig_expr, type, decl);
-      return NULL_TREE;
-    }
-  else if ((cxx_dialect >= cxx11 && cxx_dialect < cxx17)
-   && decl_linkage (decl) == lk_none)
-    {
-      if (complain & tf_error)
- error ("%qE is not a valid template argument of type %qT "
-       "because %qD has no linkage", orig_expr, type, decl);
-      return NULL_TREE;
-    }
-  /* C++17: For a non-type template-parameter of reference or pointer
-     type, the value of the constant expression shall not refer to (or
-     for a pointer type, shall not be the address of):
-       * a subobject (4.5),
-       * a temporary object (15.2),
-       * a string literal (5.13.5),
-       * the result of a typeid expression (8.2.8), or
-       * a predefined __func__ variable (11.4.1).  */
-  else if (DECL_ARTIFICIAL (decl))
-    {
-      if (complain & tf_error)
- error ("the address of %qD is not a valid template argument",
-       decl);
-      return NULL_TREE;
-    }
-  else if (!same_type_ignoring_top_level_qualifiers_p
-   (strip_array_types (TREE_TYPE (type)),
-    strip_array_types (TREE_TYPE (decl))))
-    {
-      if (complain & tf_error)
- error ("the address of the %qT subobject of %qD is not a "
-       "valid template argument", TREE_TYPE (type), decl);
-      return NULL_TREE;
-    }
-  else if (!TREE_STATIC (decl) && !DECL_EXTERNAL (decl))
-    {
-      if (complain & tf_error)
- error ("the address of %qD is not a valid template argument "
-       "because it does not have static storage duration",
-       decl);
-      return NULL_TREE;
-    }
- }
+      else if (TREE_CODE (expr) != ADDR_EXPR
+       && !INDIRECT_TYPE_P (expr_type))
+ /* Other values, like integer constants, might be valid
+   non-type arguments of some other type.  */
+ return error_mark_node;
+      else if (invalid_tparm_referent_p (type, expr, complain))
+ return NULL_TREE;
 
       expr = decayed;
 
@@ -6985,27 +7071,10 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
    itself value-dependent, since what we want here is its address.  */;
       else
  {
-  if (!DECL_P (expr))
-    {
-      if (complain & tf_error)
- error ("%qE is not a valid template argument for type %qT "
-       "because it is not an object with linkage",
-       expr, type);
-      return NULL_TREE;
-    }
-
-  /* DR 1155 allows internal linkage in C++11 and up.  */
-  linkage_kind linkage = decl_linkage (expr);
-  if (linkage < (cxx_dialect >= cxx11 ? lk_internal : lk_external))
-    {
-      if (complain & tf_error)
- error ("%qE is not a valid template argument for type %qT "
-       "because object %qD does not have linkage",
-       expr, type, expr);
-      return NULL_TREE;
-    }
-
   expr = build_address (expr);
+
+  if (invalid_tparm_referent_p (type, expr, complain))
+    return NULL_TREE;
  }
 
       if (!same_type_p (type, TREE_TYPE (expr)))
@@ -7111,6 +7180,14 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
  }
       return expr;
     }
+  else if (CLASS_TYPE_P (type))
+    {
+      /* Replace the argument with a reference to the corresponding template
+ parameter object.  */
+      expr = get_template_parm_object (expr, complain);
+      if (expr == error_mark_node)
+ return NULL_TREE;
+    }
   /* A template non-type parameter must be one of the above.  */
   else
     gcc_unreachable ();
@@ -15593,9 +15670,31 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
      first, *then* reusing the resulting type.  Doing the type
      first ensures that we handle template parameters and
      parameter pack expansions.  */
-  gcc_assert (location_wrapper_p (t));
-  tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
-  return maybe_wrap_with_location (op0, EXPR_LOCATION (t));
+  if (location_wrapper_p (t))
+    {
+      tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args,
+      complain, in_decl);
+      return maybe_wrap_with_location (op0, EXPR_LOCATION (t));
+    }
+  tree op = TREE_OPERAND (t, 0);
+  if (code == VIEW_CONVERT_EXPR
+      && TREE_CODE (op) == TEMPLATE_PARM_INDEX)
+    {
+      /* Wrapper to make a C++20 template parameter object const.  */
+      op = tsubst_copy (op, args, complain, in_decl);
+      if (TREE_CODE (op) == TEMPLATE_PARM_INDEX)
+ {
+  tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+  return build1 (code, type, op);
+ }
+      else
+ {
+  gcc_assert (CP_TYPE_CONST_P (TREE_TYPE (op)));
+  return op;
+ }
+    }
+  /* We shouldn't see any other uses of these in templates.  */
+  gcc_unreachable ();
  }
 
     case CAST_EXPR:
@@ -19042,10 +19141,9 @@ tsubst_copy_and_build (tree t,
 
     case NON_LVALUE_EXPR:
     case VIEW_CONVERT_EXPR:
-      /* We should only see these for location wrapper nodes, or within
- instantiate_non_dependent_expr (when args is NULL_TREE).  */
-      gcc_assert (location_wrapper_p (t) || args == NULL_TREE);
       if (location_wrapper_p (t))
+ /* We need to do this here as well as in tsubst_copy so we get the
+   other tsubst_copy_and_build semantics for a PARM_DECL operand.  */
  RETURN (maybe_wrap_with_location (RECUR (TREE_OPERAND (t, 0)),
   EXPR_LOCATION (t)));
       /* fallthrough.  */
@@ -24566,6 +24664,33 @@ invalid_nontype_parm_type_p (tree type, tsubst_flags_t complain)
   else if (cxx_dialect >= cxx11
    && TREE_CODE (type) == BOUND_TEMPLATE_TEMPLATE_PARM)
     return false;
+  else if (CLASS_TYPE_P (type))
+    {
+      if (cxx_dialect < cxx2a)
+ {
+  error ("non-type template parameters of class type only available "
+ "with -std=c++2a or -std=gnu++2a");
+  return true;
+ }
+      if (!complete_type_or_else (type, NULL_TREE))
+ return true;
+      if (!literal_type_p (type))
+ {
+  error ("%qT is not a valid type for a template non-type parameter "
+ "because it is not literal", type);
+  explain_non_literal_class (type);
+  return true;
+ }
+      if (cp_has_mutable_p (type))
+ {
+  error ("%qT is not a valid type for a template non-type parameter "
+ "because it has a mutable member", type);
+  return true;
+ }
+      /* FIXME check op<=> and strong structural equality once spaceship is
+ implemented.  */
+      return false;
+    }
 
   if (complain & tf_error)
     {
@@ -25361,7 +25486,10 @@ instantiation_dependent_r (tree *tp, int *walk_subtrees,
       return NULL_TREE;
 
     case TEMPLATE_PARM_INDEX:
-      return *tp;
+      if (dependent_type_p (TREE_TYPE (*tp)))
+ return *tp;
+      /* We'll check value-dependence separately.  */
+      return NULL_TREE;
 
       /* Handle expressions with type operands.  */
     case SIZEOF_EXPR:
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 0d9673db1c5..b54ecb068be 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3648,8 +3648,17 @@ finish_id_expression (tree id_expression,
       *idk = CP_ID_KIND_NONE;
       if (TREE_CODE (decl) == TEMPLATE_PARM_INDEX)
  decl = TEMPLATE_PARM_DECL (decl);
-      r = convert_from_reference (DECL_INITIAL (decl));
-
+      r = DECL_INITIAL (decl);
+      if (CLASS_TYPE_P (TREE_TYPE (r)) && !CP_TYPE_CONST_P (TREE_TYPE (r)))
+ {
+  /* If the entity is a template parameter object for a template
+     parameter of type T, the type of the expression is const T.  */
+  tree ctype = TREE_TYPE (r);
+  ctype = cp_build_qualified_type (ctype, (cp_type_quals (ctype)
+   | TYPE_QUAL_CONST));
+  r = build1 (VIEW_CONVERT_EXPR, ctype, r);
+ }
+      r = convert_from_reference (r);
       if (integral_constant_expression_p
   && !dependent_type_p (TREE_TYPE (decl))
   && !(INTEGRAL_OR_ENUMERATION_TYPE_P (TREE_TYPE (r))))
@@ -8802,7 +8811,8 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
       if (identifier_p (expr))
         expr = lookup_name (expr);
 
-      if (INDIRECT_REF_P (expr))
+      if (INDIRECT_REF_P (expr)
+  || TREE_CODE (expr) == VIEW_CONVERT_EXPR)
         /* This can happen when the expression is, e.g., "a.b". Just
            look at the underlying operand.  */
         expr = TREE_OPERAND (expr, 0);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 51af9f2015e..e9db3ea85c5 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -280,6 +280,12 @@ lvalue_kind (const_tree ref)
     case PAREN_EXPR:
       return lvalue_kind (TREE_OPERAND (ref, 0));
 
+    case TEMPLATE_PARM_INDEX:
+      if (CLASS_TYPE_P (TREE_TYPE (ref)))
+ /* A template parameter object is an lvalue.  */
+ return clk_ordinary;
+      return clk_none;
+
     default:
     default_:
       if (!TREE_TYPE (ref))
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 3f2a097e7f2..a1f74a51c9c 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -625,6 +625,9 @@ d_dump (struct demangle_component *dc, int indent)
     case DEMANGLE_COMPONENT_TEMPLATE_PARAM:
       printf ("template parameter %ld\n", dc->u.s_number.number);
       return;
+    case DEMANGLE_COMPONENT_TPARM_OBJ:
+      printf ("template parameter object\n");
+      break;
     case DEMANGLE_COMPONENT_FUNCTION_PARAM:
       printf ("function parameter %ld\n", dc->u.s_number.number);
       return;
@@ -1007,6 +1010,7 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
     case DEMANGLE_COMPONENT_GLOBAL_DESTRUCTORS:
     case DEMANGLE_COMPONENT_NULLARY:
     case DEMANGLE_COMPONENT_TRINARY_ARG2:
+    case DEMANGLE_COMPONENT_TPARM_OBJ:
       if (left == NULL)
  return NULL;
       break;
@@ -2007,6 +2011,7 @@ d_java_resource (struct d_info *di)
                   ::= TT <type>
                   ::= TI <type>
                   ::= TS <type>
+  ::= TA <template-arg>
                   ::= GV <(object) name>
                   ::= T <call-offset> <(base) encoding>
                   ::= Tc <call-offset> <call-offset> <(base) encoding>
@@ -2099,6 +2104,10 @@ d_special_name (struct d_info *di)
   return d_make_comp (di, DEMANGLE_COMPONENT_TLS_WRAPPER,
       d_name (di), NULL);
 
+ case 'A':
+  return d_make_comp (di, DEMANGLE_COMPONENT_TPARM_OBJ,
+      d_template_arg (di), NULL);
+
  default:
   return NULL;
  }
@@ -3327,11 +3336,11 @@ d_expression_1 (struct d_info *di)
     {
       /* Brace-enclosed initializer list, untyped or typed.  */
       struct demangle_component *type = NULL;
+      d_advance (di, 2);
       if (peek == 't')
  type = cplus_demangle_type (di);
       if (!d_peek_next_char (di))
  return NULL;
-      d_advance (di, 2);
       return d_make_comp (di, DEMANGLE_COMPONENT_INITIALIZER_LIST,
   type, d_exprlist (di, 'E'));
     }
@@ -4101,6 +4110,7 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
     case DEMANGLE_COMPONENT_VECTOR_TYPE:
     case DEMANGLE_COMPONENT_ARGLIST:
     case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+    case DEMANGLE_COMPONENT_TPARM_OBJ:
     case DEMANGLE_COMPONENT_INITIALIZER_LIST:
     case DEMANGLE_COMPONENT_CAST:
     case DEMANGLE_COMPONENT_CONVERSION:
@@ -4872,6 +4882,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
  }
       return;
 
+    case DEMANGLE_COMPONENT_TPARM_OBJ:
+      d_append_string (dpi, "template parameter object for ");
+      d_print_comp (dpi, options, d_left (dc));
+      return;
+
     case DEMANGLE_COMPONENT_CTOR:
       d_print_comp (dpi, options, dc->u.s_ctor.name);
       return;
diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
index b97c84c75bb..faed6697382 100644
--- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
+++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C
@@ -416,6 +416,12 @@
 #  error "__cpp_variadic_using != 201611"
 #endif
 
+// C++20 features
+
+#if __cpp_nontype_template_parameter_class != 201806
+# error "__cpp_nontype_template_parameter_class != 201806"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class1.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class1.C
new file mode 100644
index 00000000000..0c0b94d22c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class1.C
@@ -0,0 +1,33 @@
+// { dg-do compile { target c++2a } }
+
+struct A
+{
+  int i;
+  constexpr A (int i): i(i) {}
+  // auto operator<=> (const A&) = default;
+};
+
+template <A a>
+struct B
+{
+  static constexpr int i = a.i;
+  static constexpr A const* ap = &a;
+};
+
+template <A a>
+struct C
+{
+  static constexpr A const* ap = &a;
+};
+
+static_assert(B<1>::i == 1);
+static_assert(B<2>::i == 2);
+static_assert(B<1>::ap == C<1>::ap);
+static_assert(B<1>::ap != C<2>::ap);
+
+// { dg-final { scan-assembler "_Z1fP1BIXtl1ALi1EEEE" } }
+// { dg-final { scan-assembler "_ZTAXtl1ALi1EEE" } }
+const void* f(B<1> *p) {
+  constexpr int i = p->ap->i;
+  return p->ap;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class2.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class2.C
new file mode 100644
index 00000000000..2785f947544
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class2.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  // auto operator<=> (const A&) = default;
+};
+template <typename T, T t> void f()
+{
+  g(t); // { dg-error "not declared" }
+}
+
+int main()
+{
+  f<A,A{1}>();
+}
+
+// { dg-message "T t = A{1}" "" { target *-*-* } 0 }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class3.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class3.C
new file mode 100644
index 00000000000..0c0fcf6be33
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class3.C
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++2a } }
+
+struct A {
+  int i;
+  // auto operator<=> (const A&) = default;
+};
+template <A a> void g();
+template <auto t> void f()
+{
+  g<t>();
+}
+
+int main()
+{
+  f<A{1}>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class4.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class4.C
new file mode 100644
index 00000000000..fc0e7c9655d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class4.C
@@ -0,0 +1,18 @@
+// { dg-do compile { target c++2a } }
+
+template <class T>
+struct A {
+  constexpr A(T) {}
+  // auto operator<=> (const A&) = default;
+};
+template <A a> void f();
+
+int main()
+{
+  constexpr A a = 1;
+  f<a>();
+  f<1>();
+}
+
+// { dg-final { scan-assembler "_Z1fIXtl1AIiEEEEvv" } }
+// { dg-final { scan-assembler-not "_Z1fIXtlK1AIiEEEEvv" } }
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class5.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class5.C
new file mode 100644
index 00000000000..8c39cd72715
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class5.C
@@ -0,0 +1,12 @@
+// Example from P0732
+// { dg-do compile { target c++2a } }
+
+template<class T, T p> class X {
+  /* ... */
+};
+
+struct A {
+  constexpr A(const char*) {}
+  // auto operator<=> (const A&) = default;
+};
+X<A, "Pyrophoricity"> x3; // OK: string literal is a constructor argument to A
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class7.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class7.C
new file mode 100644
index 00000000000..15389e18537
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class7.C
@@ -0,0 +1,26 @@
+// Example from P0732.
+// { dg-do compile { target c++2a } }
+
+namespace std {
+  using size_t = decltype(sizeof(1));
+  template <typename CharT, std::size_t N>
+  struct basic_fixed_string
+  {
+    constexpr basic_fixed_string(const CharT (&foo)[N+1])
+    : m_data()
+    {
+      for (int i = 0; i <= N; ++i)
+ m_data[i] = foo[i];
+    }
+    // auto operator<=>(const basic_fixed_string &) = default;
+    CharT m_data[N+1];
+  };
+  template <typename CharT, std::size_t N>
+  basic_fixed_string(const CharT (&str)[N])->basic_fixed_string<CharT, N-1>;
+  template <std::size_t N>
+  using fixed_string = basic_fixed_string<char, N>;
+}
+
+template <std::basic_fixed_string Str>
+struct A {};
+using hello_A = A<"hello">;
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class8.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class8.C
new file mode 100644
index 00000000000..47fc9c7f5c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class8.C
@@ -0,0 +1,26 @@
+// If the entity is a template parameter object for a template parameter of
+// type T, the type of the expression is const T.
+
+// { dg-do compile { target c++2a } }
+
+template <class T, class U> struct same;
+template <class T> struct same<T,T> {};
+
+struct A {
+  int i;
+  // auto operator<=> (const A&) = default;
+};
+void f(A&) = delete;
+void f(const A&) { }
+
+template < A a > struct B
+{
+  B()
+  {
+    f(a);
+    same<A,decltype(a)> s;
+    same<const A&,decltype((a))> s2;
+  }
+};
+
+B<A{42}> b;
diff --git a/gcc/testsuite/g++.dg/template/crash55.C b/gcc/testsuite/g++.dg/template/crash55.C
index eac5d3cb195..5416e32cacb 100644
--- a/gcc/testsuite/g++.dg/template/crash55.C
+++ b/gcc/testsuite/g++.dg/template/crash55.C
@@ -1,6 +1,6 @@
 //PR c++/27668
 
-template<typename class T, T = T()> // { dg-error "nested-name-specifier|two or more|valid type" }
+template<typename class T, T = T()> // { dg-error "nested-name-specifier|two or more|class type|incomplete" }
 // { dg-error "cast" "" { target c++98_only } .-1 }
 struct A {};
 
diff --git a/gcc/testsuite/g++.dg/template/nontype16.C b/gcc/testsuite/g++.dg/template/nontype16.C
index 36d1e9564a0..8b3612debde 100644
--- a/gcc/testsuite/g++.dg/template/nontype16.C
+++ b/gcc/testsuite/g++.dg/template/nontype16.C
@@ -5,5 +5,5 @@ template<int> struct A
     template<typename> void foo();
 };
 
-template<> template<struct T> void A<0>::foo() {} // { dg-error "not a valid type" }
+template<> template<struct T> void A<0>::foo() {} // { dg-error "class type|incomplete" }
 
diff --git a/gcc/testsuite/g++.dg/template/nontype4.C b/gcc/testsuite/g++.dg/template/nontype4.C
index 41e3e22c000..b0d07814df1 100644
--- a/gcc/testsuite/g++.dg/template/nontype4.C
+++ b/gcc/testsuite/g++.dg/template/nontype4.C
@@ -8,7 +8,7 @@
 template <int> struct A
 {
     typedef A<0> B; // { dg-message "previous declaration" }
-    template <B> struct B {}; // { dg-error "not a valid type|typedef" }
+    template <B> struct B {}; // { dg-error "class type|incomplete|typedef" }
 };
 
 A<0> a;
diff --git a/gcc/testsuite/g++.dg/template/nontype5.C b/gcc/testsuite/g++.dg/template/nontype5.C
index f7b76259bbc..47c179ea741 100644
--- a/gcc/testsuite/g++.dg/template/nontype5.C
+++ b/gcc/testsuite/g++.dg/template/nontype5.C
@@ -8,7 +8,7 @@
 template <int> struct A
 {
     typedef A<0> B;
-    template <B> struct C {}; // { dg-error "not a valid type" }
+    template <B> struct C {}; // { dg-error "class type|incomplete" }
 };
 
 A<0> a;
diff --git a/gcc/testsuite/g++.dg/template/operator10.C b/gcc/testsuite/g++.dg/template/operator10.C
index 448b022905f..432fecdc3ac 100644
--- a/gcc/testsuite/g++.dg/template/operator10.C
+++ b/gcc/testsuite/g++.dg/template/operator10.C
@@ -3,6 +3,6 @@
 
 struct A {};
 
-template<A, typename T> int operator-(A, T); // { dg-error "not a valid type" }
+template<A, typename T> int operator-(A, T); // { dg-error "class type" "" { target c++17_down } }
 
 int i = A() - 0; // { dg-error "no match" }
diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index a3ab8e89755..fbafb9da2fb 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,8 @@
+2018-10-19  Jason Merrill  <[hidden email]>
+
+ * c-cppbuiltin.c (c_cpp_builtins): Add
+ __cpp_nontype_template_parameter_class.
+
 2018-10-31  Nathan Sidwell  <[hidden email]>
 
  * c-opts.c (c_finish_options): Force command line macro
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 4f40627a226..95149b19656 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,15 @@
 2018-11-04  Jason Merrill  <[hidden email]>
 
+ Implement P0732R2, class types in non-type template parameters.
+ * error.c (dump_simple_decl): Look through a template parm object.
+ * mangle.c (write_template_arg): Likewise.
+ (mangle_template_parm_object): New.
+ * pt.c (template_parm_object_p, get_template_parm_object): New.
+ (invalid_tparm_referent_p): Factor from convert_nontype_argument.
+ (convert_nontype_argument, invalid_nontype_parm_type_p): Handle
+ class-type template arguments.
+ * tree.c (lvalue_kind): Likewise.
+
  * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
  * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
  (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
diff --git a/include/ChangeLog b/include/ChangeLog
index 4584f7cb8af..19f515d59b3 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,3 +1,8 @@
+2018-10-19  Jason Merrill  <[hidden email]>
+
+ * demangle.h (enum demangle_component_type): Add
+ DEMANGLE_COMPONENT_TPARM_OBJ.
+
 2018-10-29  David Malcolm  <[hidden email]>
 
  * unique-ptr.h (gnu::move): Generalize so it applies to all
diff --git a/libiberty/ChangeLog b/libiberty/ChangeLog
index a5f8742cee7..91211c87e7d 100644
--- a/libiberty/ChangeLog
+++ b/libiberty/ChangeLog
@@ -1,3 +1,11 @@
+2018-10-23  Jason Merrill  <[hidden email]>
+
+ Implement P0732R2, class types in non-type template parameters.
+ * cp-demangle.c (d_dump, d_make_comp, d_count_templates_scopes)
+ (d_print_comp_inner): Handle DEMANGLE_COMPONENT_TPARM_OBJ.
+ (d_special_name): Handle TA.
+ (d_expresion_1): Fix demangling of brace-enclosed initializer list.
+
 2018-10-31  Joseph Myers  <[hidden email]>
 
  PR bootstrap/82856
diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected
index b62561c6d4d..40038365c83 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -4667,6 +4667,12 @@ void eat<int*, Foo()::{lambda(auto:1*, auto:2*)#6}>(int*&, Foo()::{lambda(auto:1
 _Z3eatIPiZ3BarIsEvvEUlPsPT_PT0_E0_EvRS3_RS5_
 void eat<int*, Bar<short>()::{lambda(short*, auto:1*, auto:2*)#2}>(int*&, Bar<short>()::{lambda(short*, auto:1*, auto:2*)#2}&)
 
+_Z1fP1BIXtl1ALi1EEEE
+f(B<A{1}>*)
+
+_ZTAXtl1ALi1EEE
+template parameter object for A{1}
+
 # PR 77489
 _ZZ3foovE8localVar_9
 foo()::localVar
--
2.17.2

Reply | Threaded
Open this post in threaded view
|

[PATCH 3/3] Implement UDL changes from P0732R2.

Jason Merrill
In reply to this post by Jason Merrill
Implementing the UDL changes was pretty straightforward; I simplified
cp_parser_userdef_string_literal using the releasing_vec type from mangle.c.

While looking at this, I realized that the string UDL template taking a
character pack that we implemented for C++14 didn't actually make it into
C++14, so I've added a pedwarn for it and no longer suggest it in the
diagnostic about an invalid UDL template.

        * cp-tree.h (struct releasing_vec): Move from mangle.c.
        Add get_ref method.
        * parser.c (cp_parser_userdef_string_literal): Use it.  Handle
        passing the string to a single template parameter of class type.
        (cp_parser_template_declaration_after_parameters): Allow it.
        Pedwarn about the character pack template that was proposed but not
        accepted for C++14, and don't suggest it.
---
 gcc/cp/cp-tree.h                              | 28 ++++++++
 gcc/cp/mangle.c                               | 24 -------
 gcc/cp/parser.c                               | 70 ++++++++++++-------
 gcc/testsuite/g++.dg/cpp0x/udlit-overflow.C   | 36 +++++-----
 gcc/testsuite/g++.dg/cpp1y/pr58708.C          |  1 +
 gcc/testsuite/g++.dg/cpp1y/pr59867.C          |  1 +
 .../g++.dg/cpp1y/udlit-char-template-sfinae.C |  1 +
 ...it-char-template-vs-std-literal-operator.C |  1 +
 .../g++.dg/cpp1y/udlit-char-template.C        |  1 +
 .../g++.dg/cpp1y/udlit-char-template2.C       |  1 +
 gcc/testsuite/g++.dg/cpp2a/nontype-class6.C   | 17 +++++
 gcc/cp/ChangeLog                              |  9 +++
 12 files changed, 121 insertions(+), 69 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class6.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 61b431e5f9d..a895d0042ab 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -871,6 +871,34 @@ struct named_decl_hash : ggc_remove <tree>
   static void mark_deleted (value_type) { gcc_unreachable (); }
 };
 
+/* Simplified unique_ptr clone to release a tree vec on exit.  */
+
+struct releasing_vec
+{
+  typedef vec<tree, va_gc> vec_t;
+
+  releasing_vec (vec_t *v): v(v) { }
+  releasing_vec (): v(make_tree_vector ()) { }
+
+  /* Copy ops are deliberately declared but not defined,
+     copies must always be elided.  */
+  releasing_vec (const releasing_vec &);
+  releasing_vec &operator= (const releasing_vec &);
+
+  vec_t &operator* () const { return *v; }
+  vec_t *operator-> () const { return v; }
+  vec_t *get() const { return v; }
+  operator vec_t *() const { return v; }
+  tree& operator[] (unsigned i) const { return (*v)[i]; }
+
+  /* Necessary for use with vec** and vec*& interfaces.  */
+  vec_t *&get_ref () { return v; }
+
+  ~releasing_vec() { release_tree_vector (v); }
+private:
+  vec_t *v;
+};
+
 struct GTY(()) tree_template_decl {
   struct tree_decl_common common;
   tree arguments;
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 1b323015ded..b9d8ee20116 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -1545,30 +1545,6 @@ write_abi_tags (tree tags)
   release_tree_vector (vec);
 }
 
-/* Simplified unique_ptr clone to release a tree vec on exit.  */
-
-struct releasing_vec
-{
-  typedef vec<tree, va_gc> vec_t;
-
-  releasing_vec (vec_t *v): v(v) { }
-  releasing_vec (): v(make_tree_vector ()) { }
-
-  /* Copy constructor is deliberately declared but not defined,
-     copies must always be elided.  */
-  releasing_vec (const releasing_vec &);
-
-  vec_t &operator* () const { return *v; }
-  vec_t *operator-> () const { return v; }
-  vec_t *get () const { return v; }
-  operator vec_t *() const { return v; }
-  tree& operator[] (unsigned i) const { return (*v)[i]; }
-
-  ~releasing_vec() { release_tree_vector (v); }
-private:
-  vec_t *v;
-};
-
 /* True iff the TREE_LISTS T1 and T2 of ABI tags are equivalent.  */
 
 static bool
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 5ea8e8ca012..30a47662f55 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -4565,40 +4565,47 @@ cp_parser_userdef_string_literal (tree literal)
   tree value = USERDEF_LITERAL_VALUE (literal);
   int len = TREE_STRING_LENGTH (value)
  / TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (value)))) - 1;
-  tree decl, result;
-  vec<tree, va_gc> *args;
+  tree decl;
 
   /* Build up a call to the user-defined operator.  */
   /* Lookup the name we got back from the id-expression.  */
-  args = make_tree_vector ();
+  releasing_vec rargs;
+  vec<tree, va_gc> *&args = rargs.get_ref();
   vec_safe_push (args, value);
   vec_safe_push (args, build_int_cst (size_type_node, len));
   decl = lookup_literal_operator (name, args);
 
   if (decl && decl != error_mark_node)
-    {
-      result = finish_call_expr (decl, &args, false, true,
- tf_warning_or_error);
-      release_tree_vector (args);
-      return result;
-    }
-  release_tree_vector (args);
+    return finish_call_expr (decl, &args, false, true,
+     tf_warning_or_error);
 
-  /* Look for a template function with typename parameter CharT
-     and parameter pack CharT...  Call the function with
-     template parameter characters representing the string.  */
-  args = make_tree_vector ();
+  /* Look for a suitable template function, either (C++20) with a single
+     parameter of class type, or (N3599) with typename parameter CharT and
+     parameter pack CharT...  */
+  args->truncate (0);
   decl = lookup_literal_operator (name, args);
   if (decl && decl != error_mark_node)
     {
-      tree tmpl_args = make_string_pack (value);
+      /* Use resolve_nondeduced_context to try to choose one form of template
+ or the other.  */
+      tree tmpl_args = make_tree_vec (1);
+      TREE_VEC_ELT (tmpl_args, 0) = value;
       decl = lookup_template_function (decl, tmpl_args);
-      result = finish_call_expr (decl, &args, false, true,
- tf_warning_or_error);
-      release_tree_vector (args);
-      return result;
+      tree res = resolve_nondeduced_context (decl, tf_none);
+      if (DECL_P (res))
+ decl = res;
+      else
+ {
+  TREE_OPERAND (decl, 1) = make_string_pack (value);
+  res = resolve_nondeduced_context (decl, tf_none);
+  if (DECL_P (res))
+    decl = res;
+ }
+      if (!DECL_P (decl) && cxx_dialect > cxx17)
+ TREE_OPERAND (decl, 1) = tmpl_args;
+      return finish_call_expr (decl, &args, false, true,
+       tf_warning_or_error);
     }
-  release_tree_vector (args);
 
   error ("unable to find string literal operator %qD with %qT, %qT arguments",
  name, TREE_TYPE (value), size_type_node);
@@ -27222,8 +27229,12 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
     {
       tree parm_list = TREE_VEC_ELT (parameter_list, 0);
       tree parm = INNERMOST_TEMPLATE_PARMS (parm_list);
-      if (TREE_TYPE (parm) != char_type_node
-  || !TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)))
+      if (CLASS_TYPE_P (TREE_TYPE (parm)))
+ /* OK, C++20 string literal operator template.  We don't need
+   to warn in lower dialects here because we will have already
+   warned about the template parameter.  */;
+      else if (TREE_TYPE (parm) != char_type_node
+       || !TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)))
  ok = false;
     }
   else if (num_parms == 2 && cxx_dialect >= cxx14)
@@ -27236,20 +27247,25 @@ cp_parser_template_declaration_after_parameters (cp_parser* parser,
   || TREE_TYPE (parm) != TREE_TYPE (type)
   || !TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm)))
  ok = false;
+      else
+ /* http://cplusplus.github.io/EWG/ewg-active.html#66  */
+ pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wpedantic,
+ "ISO C++ did not adopt string literal operator templa"
+ "tes taking an argument pack of characters");
     }
   else
     ok = false;
  }
       if (!ok)
  {
-  if (cxx_dialect >= cxx14)
-    error ("literal operator template %qD has invalid parameter list."
-   "  Expected non-type template argument pack <char...>"
-   " or <typename CharT, CharT...>",
+  if (cxx_dialect > cxx17)
+    error ("literal operator template %qD has invalid parameter list;"
+   "  Expected non-type template parameter pack <char...> "
+   "  or single non-type parameter of class type",
    decl);
   else
     error ("literal operator template %qD has invalid parameter list."
-   "  Expected non-type template argument pack <char...>",
+   "  Expected non-type template parameter pack <char...>",
    decl);
  }
     }
diff --git a/gcc/testsuite/g++.dg/cpp0x/udlit-overflow.C b/gcc/testsuite/g++.dg/cpp0x/udlit-overflow.C
index 057978c3275..1b9888b7159 100644
--- a/gcc/testsuite/g++.dg/cpp0x/udlit-overflow.C
+++ b/gcc/testsuite/g++.dg/cpp0x/udlit-overflow.C
@@ -1,19 +1,19 @@
 // { dg-do compile { target c++11 } }
-// PR c++/52654
-
-int
-operator"" _w(const char*)
-{ return 0; }
-
-template<char...>
-  int
-  operator"" _tw()
-  { return 0; }
-
-int i = 12345678901234567890123456789012345678901234567890_w;
-int j = 12345678901234567890123456789.012345678901234567890e+1234567890_w;
-int k = 12345678901234567890123456789.012345678901234567890e-1234567890_w;
-
-int ti = 12345678901234567890123456789012345678901234567890_tw;
-int tj = 12345678901234567890123456789.012345678901234567890e+1234567890_tw;
-int tk = 12345678901234567890123456789.012345678901234567890e-1234567890_tw;
+// PR c++/52654
+
+int
+operator"" _w(const char*)
+{ return 0; }
+
+template<char...>
+  int
+  operator"" _tw()
+  { return 0; }
+
+int i = 12345678901234567890123456789012345678901234567890_w;
+int j = 12345678901234567890123456789.012345678901234567890e+1234567890_w;
+int k = 12345678901234567890123456789.012345678901234567890e-1234567890_w;
+
+int ti = 12345678901234567890123456789012345678901234567890_tw;
+int tj = 12345678901234567890123456789.012345678901234567890e+1234567890_tw;
+int tk = 12345678901234567890123456789.012345678901234567890e-1234567890_tw;
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr58708.C b/gcc/testsuite/g++.dg/cpp1y/pr58708.C
index b46e54bbca3..f1fc3b46227 100644
--- a/gcc/testsuite/g++.dg/cpp1y/pr58708.C
+++ b/gcc/testsuite/g++.dg/cpp1y/pr58708.C
@@ -1,4 +1,5 @@
 // { dg-do run { target c++14 } }
+// { dg-options -w }
 
 template<typename, typename>
   struct is_same
diff --git a/gcc/testsuite/g++.dg/cpp1y/pr59867.C b/gcc/testsuite/g++.dg/cpp1y/pr59867.C
index 2c4f1d046bf..ee468aabb2b 100644
--- a/gcc/testsuite/g++.dg/cpp1y/pr59867.C
+++ b/gcc/testsuite/g++.dg/cpp1y/pr59867.C
@@ -1,5 +1,6 @@
 // PR c++/59867
 // { dg-do compile { target c++14 } }
+// { dg-options -w }
 
 using namespace std;
 
diff --git a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C
index a9c577fd110..b523858e9c0 100644
--- a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C
+++ b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-sfinae.C
@@ -1,4 +1,5 @@
 // { dg-do run { target c++14 } }
+// { dg-options -w }
 
 #include <cassert>
 
diff --git a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C
index 6e3ffe4ecd3..c5a67c96cf6 100644
--- a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C
+++ b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template-vs-std-literal-operator.C
@@ -1,4 +1,5 @@
 // { dg-do run { target c++14 } }
+// { dg-options -w }
 
 #include <cassert>
 
diff --git a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template.C b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template.C
index 989b9babd1f..6b7b5ecec6f 100644
--- a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template.C
+++ b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template.C
@@ -1,4 +1,5 @@
 // { dg-do compile { target c++14 } }
+// { dg-options -w }
 
 template<typename CharT, CharT... String>
   int
diff --git a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template2.C b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template2.C
index 06c13261156..d3c4c67ea26 100644
--- a/gcc/testsuite/g++.dg/cpp1y/udlit-char-template2.C
+++ b/gcc/testsuite/g++.dg/cpp1y/udlit-char-template2.C
@@ -1,5 +1,6 @@
 // PR c++/85864
 // { dg-do compile { target c++14 } }
+// { dg-options -w }
 
 template<class T, T... S> struct String_template {};
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class6.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class6.C
new file mode 100644
index 00000000000..246fab6c178
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class6.C
@@ -0,0 +1,17 @@
+// { dg-do compile { target c++2a } }
+
+struct A {
+  char ar[10];
+  constexpr A (const char *p) : ar()
+  {
+    for (int i = 0; i < 10; ++i)
+      if ((ar[i] = p[i]) == 0)
+ break;
+  }
+  // auto operator<=> (const A&) = default;
+};
+
+template <A a> constexpr A operator "" _sh() { return a; }
+
+constexpr auto a = "foo"_sh;
+static_assert (a.ar[0] == 'f');
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 95149b19656..b0dc668d9df 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,14 @@
 2018-11-04  Jason Merrill  <[hidden email]>
 
+ Implement UDL changes from P0732R2.
+ * cp-tree.h (struct releasing_vec): Move from mangle.c.
+ Add get_ref method.
+ * parser.c (cp_parser_userdef_string_literal): Use it.  Handle
+ passing the string to a single template parameter of class type.
+ (cp_parser_template_declaration_after_parameters): Allow it.
+ Pedwarn about the character pack template that was proposed but not
+ accepted for C++14, and don't suggest it.
+
  Implement P0732R2, class types in non-type template parameters.
  * error.c (dump_simple_decl): Look through a template parm object.
  * mangle.c (write_template_arg): Likewise.
--
2.17.2

Reply | Threaded
Open this post in threaded view
|

Re: [C++ PATCH 1/3] Fix various latent issues revealed by P0732 work.

Matthew Malcomson
In reply to this post by Jason Merrill
(Third attempt to put this on the mailing list now -- today is not a good day for my email skills :-[)

Hi there,

This patch has caused a few g++ and libstdc++ regression test failures on arm,

I've included the g++ failures below.

Do you mind looking into this?
Cheers,

Matthew


dg-cmp-results.sh: Verbosity is 2, Variant is ""

Older log file: just-before-problemcase-g++.sum
Test Run By matthew on Thu Nov  8 16:45:40 2018
Target is arm-none-eabi
Host   is x86_64-pc-linux-gnu

Newer log file: g++-after-problem.sum
Test Run By matthew on Thu Nov  8 21:36:14 2018
Target is arm-none-eabi
Host   is x86_64-pc-linux-gnu

PASS->FAIL: g++.dg/cpp0x/constexpr-static12.C  -std=c++14 scan-assembler-not _ZNSt10unique_ptrC1Ei
PASS->FAIL: g++.dg/cpp0x/constexpr-static12.C  -std=c++17 scan-assembler-not _ZNSt10unique_ptrC1Ei
PASS->FAIL: g++.dg/cpp0x/constexpr-static3.C  -std=gnu++14 scan-assembler-not static_initialization
PASS->FAIL: g++.dg/cpp0x/constexpr-static3.C  -std=gnu++17 scan-assembler-not static_initialization
PASS->FAIL: g++.dg/cpp0x/constexpr-static.C  -std=gnu++14 scan-assembler-not static_initialization
PASS->FAIL: g++.dg/cpp0x/constexpr-static.C  -std=gnu++17 scan-assembler-not static_initialization
PASS->FAIL: g++.dg/cpp0x/implicit13.C  -std=c++14 scan-assembler-not _ZN1BC1Ev
PASS->FAIL: g++.dg/cpp0x/implicit13.C  -std=c++17 scan-assembler-not _ZN1BC1Ev
NA->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++14 (internal compiler error)
PASS->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++14 (test for excess errors)
NA->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++17 (internal compiler error)
PASS->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++17 (test for excess errors)
PASS->FAIL: g++.dg/cpp1y/constexpr-empty3.C  -std=c++14 scan-assembler-not static_init
PASS->FAIL: g++.dg/cpp1y/constexpr-empty3.C  -std=c++17 scan-assembler-not static_init


On 05/11/18 02:06, Jason Merrill wrote:
The initialized_type hunk fixes handling of void AGGR_INIT_EXPRs that call a
non-constructor; an AGGR_INIT_EXPR can have void type if its initialization
semantics are more complicated than just expanding the call.

The cxx_eval_vec_init_1 hunk corrects AGGR_INIT_EXPRs that were
nonsensically built to initialize an object of void type.  And the
build_aggr_init_expr hunk makes sure we don't do that again.

The ocp_convert and cxx_eval_outermost_constant_expr hunks deal with making
sure that a constant CONSTRUCTOR has the right type.

Tested x86_64-pc-linux-gnu, applying to trunk.

    * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
    * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
    (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
    (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
    right type.  Don't wrap a CONSTRUCTOR if one was passed in.
    * tree.c (build_aggr_init_expr): Check for void.
---
  gcc/cp/constexpr.c | 22 +++++++++++++---------
  gcc/cp/cvt.c       | 10 +++++++++-
  gcc/cp/tree.c      |  2 ++
  gcc/cp/ChangeLog   |  7 +++++++
  4 files changed, 31 insertions(+), 10 deletions(-)

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 7692b1727da..4fb1ba527e3 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2778,8 +2778,10 @@ initialized_type (tree t)
  {
    if (TYPE_P (t))
      return t;
-  tree type = cv_unqualified (TREE_TYPE (t));
-  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
+  tree type = TREE_TYPE (t);
+  if (!VOID_TYPE_P (type))
+    /* No need to look deeper.  */;
+  else if (TREE_CODE (t) == CALL_EXPR)
      {
        /* A constructor call has void type, so we need to look deeper.  */
        tree fn = get_function_named_in_call (t);
@@ -2787,7 +2789,9 @@ initialized_type (tree t)
        && DECL_CXX_CONSTRUCTOR_P (fn))
      type = DECL_CONTEXT (fn);
      }
-  return type;
+  else if (TREE_CODE (t) == AGGR_INIT_EXPR)
+    type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
+  return cv_unqualified (type);
  }
    /* We're about to initialize element INDEX of an array or class from VALUE.
@@ -3000,7 +3004,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
                      &argvec, elttype, LOOKUP_NORMAL,
                      complain);
        release_tree_vector (argvec);
-      init = build_aggr_init_expr (TREE_TYPE (init), init);
+      init = build_aggr_init_expr (elttype, init);
        pre_init = true;
      }
  @@ -5089,7 +5093,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
      r = build_nop (TREE_TYPE (r), r);
        TREE_CONSTANT (r) = false;
      }
-  else if (non_constant_p || r == t)
+  else if (non_constant_p)
      return t;
      if (should_unshare)
@@ -5097,18 +5101,18 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
      if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
      {
+      r = adjust_temp_type (type, r);
        if (TREE_CODE (t) == TARGET_EXPR
        && TARGET_EXPR_INITIAL (t) == r)
      return t;
-      else
+      else if (TREE_CODE (t) != CONSTRUCTOR)
      {
        r = get_target_expr (r);
        TREE_CONSTANT (r) = true;
-      return r;
      }
      }
-  else
-    return r;
+
+  return r;
  }
    /* Returns true if T is a valid subexpression of a constant expression,
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 315b0d6a65a..b04e9a70652 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -725,7 +725,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
      /* We need a new temporary; don't take this shortcut.  */;
    else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
      {
-      if (same_type_p (type, TREE_TYPE (e)))
+      tree etype = TREE_TYPE (e);
+      if (same_type_p (type, etype))
      /* The call to fold will not always remove the NOP_EXPR as
         might be expected, since if one of the types is a typedef;
         the comparison in fold is just equality of pointers, not a
@@ -743,9 +744,16 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
      {
        /* Don't build a NOP_EXPR of class type.  Instead, change the
           type of the temporary.  */
+      gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
        TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
        return e;
      }
+      else if (TREE_CODE (e) == CONSTRUCTOR)
+    {
+      gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
+      TREE_TYPE (e) = type;
+      return e;
+    }
        else
      {
        /* We shouldn't be treating objects of ADDRESSABLE type as
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 74018e97bb7..51af9f2015e 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -576,6 +576,8 @@ build_aggr_init_expr (tree type, tree init)
    tree rval;
    int is_ctor;
  +  gcc_assert (!VOID_TYPE_P (type));
+
    /* Don't build AGGR_INIT_EXPR in a template.  */
    if (processing_template_decl)
      return init;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 7e9c0e2642a..4f40627a226 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,12 @@
  2018-11-04  Jason Merrill  <[hidden email]><mailto:[hidden email]>
  +    * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
+    * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
+    (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
+    (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
+    right type.  Don't wrap a CONSTRUCTOR if one was passed in.
+    * tree.c (build_aggr_init_expr): Check for void.
+
      PR c++/60503 - wrong lambda attribute syntax.
      * parser.c (cp_parser_lambda_declarator_opt): Fix attribute
      handling.

base-commit: 703d2f69fad4772dc4aedb5327b5e3d88e8e1843


On 05/11/18 02:06, Jason Merrill wrote:

The initialized_type hunk fixes handling of void AGGR_INIT_EXPRs that call a
non-constructor; an AGGR_INIT_EXPR can have void type if its initialization
semantics are more complicated than just expanding the call.

The cxx_eval_vec_init_1 hunk corrects AGGR_INIT_EXPRs that were
nonsensically built to initialize an object of void type.  And the
build_aggr_init_expr hunk makes sure we don't do that again.

The ocp_convert and cxx_eval_outermost_constant_expr hunks deal with making
sure that a constant CONSTRUCTOR has the right type.

Tested x86_64-pc-linux-gnu, applying to trunk.

        * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
        * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
        (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
        (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
        right type.  Don't wrap a CONSTRUCTOR if one was passed in.
        * tree.c (build_aggr_init_expr): Check for void.
---
 gcc/cp/constexpr.c | 22 +++++++++++++---------
 gcc/cp/cvt.c       | 10 +++++++++-
 gcc/cp/tree.c      |  2 ++
 gcc/cp/ChangeLog   |  7 +++++++
 4 files changed, 31 insertions(+), 10 deletions(-)

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 7692b1727da..4fb1ba527e3 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2778,8 +2778,10 @@ initialized_type (tree t)
 {
   if (TYPE_P (t))
     return t;
-  tree type = cv_unqualified (TREE_TYPE (t));
-  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
+  tree type = TREE_TYPE (t);
+  if (!VOID_TYPE_P (type))
+    /* No need to look deeper.  */;
+  else if (TREE_CODE (t) == CALL_EXPR)
     {
       /* A constructor call has void type, so we need to look deeper.  */
       tree fn = get_function_named_in_call (t);
@@ -2787,7 +2789,9 @@ initialized_type (tree t)
          && DECL_CXX_CONSTRUCTOR_P (fn))
        type = DECL_CONTEXT (fn);
     }
-  return type;
+  else if (TREE_CODE (t) == AGGR_INIT_EXPR)
+    type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
+  return cv_unqualified (type);
 }

 /* We're about to initialize element INDEX of an array or class from VALUE.
@@ -3000,7 +3004,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
                                        &argvec, elttype, LOOKUP_NORMAL,
                                        complain);
       release_tree_vector (argvec);
-      init = build_aggr_init_expr (TREE_TYPE (init), init);
+      init = build_aggr_init_expr (elttype, init);
       pre_init = true;
     }

@@ -5089,7 +5093,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
        r = build_nop (TREE_TYPE (r), r);
       TREE_CONSTANT (r) = false;
     }
-  else if (non_constant_p || r == t)
+  else if (non_constant_p)
     return t;

   if (should_unshare)
@@ -5097,18 +5101,18 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,

   if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
     {
+      r = adjust_temp_type (type, r);
       if (TREE_CODE (t) == TARGET_EXPR
          && TARGET_EXPR_INITIAL (t) == r)
        return t;
-      else
+      else if (TREE_CODE (t) != CONSTRUCTOR)
        {
          r = get_target_expr (r);
          TREE_CONSTANT (r) = true;
-         return r;
        }
     }
-  else
-    return r;
+
+  return r;
 }

 /* Returns true if T is a valid subexpression of a constant expression,
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 315b0d6a65a..b04e9a70652 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -725,7 +725,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
     /* We need a new temporary; don't take this shortcut.  */;
   else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
     {
-      if (same_type_p (type, TREE_TYPE (e)))
+      tree etype = TREE_TYPE (e);
+      if (same_type_p (type, etype))
        /* The call to fold will not always remove the NOP_EXPR as
           might be expected, since if one of the types is a typedef;
           the comparison in fold is just equality of pointers, not a
@@ -743,9 +744,16 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
        {
          /* Don't build a NOP_EXPR of class type.  Instead, change the
             type of the temporary.  */
+         gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
          TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
          return e;
        }
+      else if (TREE_CODE (e) == CONSTRUCTOR)
+       {
+         gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
+         TREE_TYPE (e) = type;
+         return e;
+       }
       else
        {
          /* We shouldn't be treating objects of ADDRESSABLE type as
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 74018e97bb7..51af9f2015e 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -576,6 +576,8 @@ build_aggr_init_expr (tree type, tree init)
   tree rval;
   int is_ctor;

+  gcc_assert (!VOID_TYPE_P (type));
+
   /* Don't build AGGR_INIT_EXPR in a template.  */
   if (processing_template_decl)
     return init;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 7e9c0e2642a..4f40627a226 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,12 @@
 2018-11-04  Jason Merrill  <[hidden email]><mailto:[hidden email]>

+       * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
+       * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
+       (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
+       (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
+       right type.  Don't wrap a CONSTRUCTOR if one was passed in.
+       * tree.c (build_aggr_init_expr): Check for void.
+
        PR c++/60503 - wrong lambda attribute syntax.
        * parser.c (cp_parser_lambda_declarator_opt): Fix attribute
        handling.

base-commit: 703d2f69fad4772dc4aedb5327b5e3d88e8e1843

Reply | Threaded
Open this post in threaded view
|

Re: [C++ PATCH 1/3] Fix various latent issues revealed by P0732 work.

Kyrill  Tkachov-2

On 09/11/18 10:28, Matthew Malcomson wrote:

> (Third attempt to put this on the mailing list now -- today is not a good day for my email skills :-[)
>
> Hi there,
>
> This patch has caused a few g++ and libstdc++ regression test failures on arm,
>
> I've included the g++ failures below.
>
> Do you mind looking into this?
> Cheers,
>
> Matthew
>
>
> dg-cmp-results.sh: Verbosity is 2, Variant is ""
>
> Older log file: just-before-problemcase-g++.sum
> Test Run By matthew on Thu Nov  8 16:45:40 2018
> Target is arm-none-eabi
> Host   is x86_64-pc-linux-gnu
>
> Newer log file: g++-after-problem.sum
> Test Run By matthew on Thu Nov  8 21:36:14 2018
> Target is arm-none-eabi
> Host   is x86_64-pc-linux-gnu
>
> PASS->FAIL: g++.dg/cpp0x/constexpr-static12.C  -std=c++14 scan-assembler-not _ZNSt10unique_ptrC1Ei
> PASS->FAIL: g++.dg/cpp0x/constexpr-static12.C  -std=c++17 scan-assembler-not _ZNSt10unique_ptrC1Ei
> PASS->FAIL: g++.dg/cpp0x/constexpr-static3.C -std=gnu++14 scan-assembler-not static_initialization
> PASS->FAIL: g++.dg/cpp0x/constexpr-static3.C -std=gnu++17 scan-assembler-not static_initialization
> PASS->FAIL: g++.dg/cpp0x/constexpr-static.C  -std=gnu++14 scan-assembler-not static_initialization
> PASS->FAIL: g++.dg/cpp0x/constexpr-static.C  -std=gnu++17 scan-assembler-not static_initialization
> PASS->FAIL: g++.dg/cpp0x/implicit13.C  -std=c++14 scan-assembler-not _ZN1BC1Ev
> PASS->FAIL: g++.dg/cpp0x/implicit13.C  -std=c++17 scan-assembler-not _ZN1BC1Ev
> NA->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++14 (internal compiler error)
> PASS->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++14 (test for excess errors)
> NA->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++17 (internal compiler error)
> PASS->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++17 (test for excess errors)
> PASS->FAIL: g++.dg/cpp1y/constexpr-empty3.C  -std=c++14 scan-assembler-not static_init
> PASS->FAIL: g++.dg/cpp1y/constexpr-empty3.C  -std=c++17 scan-assembler-not static_init
>
>

I believe these are tracked in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87935 FWIW.

Kyrill

> On 05/11/18 02:06, Jason Merrill wrote:
> The initialized_type hunk fixes handling of void AGGR_INIT_EXPRs that call a
> non-constructor; an AGGR_INIT_EXPR can have void type if its initialization
> semantics are more complicated than just expanding the call.
>
> The cxx_eval_vec_init_1 hunk corrects AGGR_INIT_EXPRs that were
> nonsensically built to initialize an object of void type. And the
> build_aggr_init_expr hunk makes sure we don't do that again.
>
> The ocp_convert and cxx_eval_outermost_constant_expr hunks deal with making
> sure that a constant CONSTRUCTOR has the right type.
>
> Tested x86_64-pc-linux-gnu, applying to trunk.
>
>     * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
>     * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
>     (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
>     (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
>     right type.  Don't wrap a CONSTRUCTOR if one was passed in.
>     * tree.c (build_aggr_init_expr): Check for void.
> ---
>   gcc/cp/constexpr.c | 22 +++++++++++++---------
>   gcc/cp/cvt.c       | 10 +++++++++-
>   gcc/cp/tree.c      |  2 ++
>   gcc/cp/ChangeLog   |  7 +++++++
>   4 files changed, 31 insertions(+), 10 deletions(-)
>
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 7692b1727da..4fb1ba527e3 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -2778,8 +2778,10 @@ initialized_type (tree t)
>   {
>     if (TYPE_P (t))
>       return t;
> -  tree type = cv_unqualified (TREE_TYPE (t));
> -  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
> +  tree type = TREE_TYPE (t);
> +  if (!VOID_TYPE_P (type))
> +    /* No need to look deeper.  */;
> +  else if (TREE_CODE (t) == CALL_EXPR)
>       {
>         /* A constructor call has void type, so we need to look deeper.  */
>         tree fn = get_function_named_in_call (t);
> @@ -2787,7 +2789,9 @@ initialized_type (tree t)
>         && DECL_CXX_CONSTRUCTOR_P (fn))
>       type = DECL_CONTEXT (fn);
>       }
> -  return type;
> +  else if (TREE_CODE (t) == AGGR_INIT_EXPR)
> +    type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
> +  return cv_unqualified (type);
>   }
>     /* We're about to initialize element INDEX of an array or class from VALUE.
> @@ -3000,7 +3004,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
>                       &argvec, elttype, LOOKUP_NORMAL,
>                       complain);
>         release_tree_vector (argvec);
> -      init = build_aggr_init_expr (TREE_TYPE (init), init);
> +      init = build_aggr_init_expr (elttype, init);
>         pre_init = true;
>       }
>   @@ -5089,7 +5093,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
>       r = build_nop (TREE_TYPE (r), r);
>         TREE_CONSTANT (r) = false;
>       }
> -  else if (non_constant_p || r == t)
> +  else if (non_constant_p)
>       return t;
>       if (should_unshare)
> @@ -5097,18 +5101,18 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
>       if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
>       {
> +      r = adjust_temp_type (type, r);
>         if (TREE_CODE (t) == TARGET_EXPR
>         && TARGET_EXPR_INITIAL (t) == r)
>       return t;
> -      else
> +      else if (TREE_CODE (t) != CONSTRUCTOR)
>       {
>         r = get_target_expr (r);
>         TREE_CONSTANT (r) = true;
> -      return r;
>       }
>       }
> -  else
> -    return r;
> +
> +  return r;
>   }
>     /* Returns true if T is a valid subexpression of a constant expression,
> diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
> index 315b0d6a65a..b04e9a70652 100644
> --- a/gcc/cp/cvt.c
> +++ b/gcc/cp/cvt.c
> @@ -725,7 +725,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
>       /* We need a new temporary; don't take this shortcut. */;
>     else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
>       {
> -      if (same_type_p (type, TREE_TYPE (e)))
> +      tree etype = TREE_TYPE (e);
> +      if (same_type_p (type, etype))
>       /* The call to fold will not always remove the NOP_EXPR as
>          might be expected, since if one of the types is a typedef;
>          the comparison in fold is just equality of pointers, not a
> @@ -743,9 +744,16 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
>       {
>         /* Don't build a NOP_EXPR of class type.  Instead, change the
>            type of the temporary.  */
> +      gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
>         TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
>         return e;
>       }
> +      else if (TREE_CODE (e) == CONSTRUCTOR)
> +    {
> +      gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
> +      TREE_TYPE (e) = type;
> +      return e;
> +    }
>         else
>       {
>         /* We shouldn't be treating objects of ADDRESSABLE type as
> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> index 74018e97bb7..51af9f2015e 100644
> --- a/gcc/cp/tree.c
> +++ b/gcc/cp/tree.c
> @@ -576,6 +576,8 @@ build_aggr_init_expr (tree type, tree init)
>     tree rval;
>     int is_ctor;
>   +  gcc_assert (!VOID_TYPE_P (type));
> +
>     /* Don't build AGGR_INIT_EXPR in a template.  */
>     if (processing_template_decl)
>       return init;
> diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
> index 7e9c0e2642a..4f40627a226 100644
> --- a/gcc/cp/ChangeLog
> +++ b/gcc/cp/ChangeLog
> @@ -1,5 +1,12 @@
>   2018-11-04  Jason Merrill <[hidden email]><mailto:[hidden email]>
>   +    * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
> +    * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
> +    (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
> +    (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
> +    right type.  Don't wrap a CONSTRUCTOR if one was passed in.
> +    * tree.c (build_aggr_init_expr): Check for void.
> +
>       PR c++/60503 - wrong lambda attribute syntax.
>       * parser.c (cp_parser_lambda_declarator_opt): Fix attribute
>       handling.
>
> base-commit: 703d2f69fad4772dc4aedb5327b5e3d88e8e1843
>
>
> On 05/11/18 02:06, Jason Merrill wrote:
>
> The initialized_type hunk fixes handling of void AGGR_INIT_EXPRs that call a
> non-constructor; an AGGR_INIT_EXPR can have void type if its initialization
> semantics are more complicated than just expanding the call.
>
> The cxx_eval_vec_init_1 hunk corrects AGGR_INIT_EXPRs that were
> nonsensically built to initialize an object of void type. And the
> build_aggr_init_expr hunk makes sure we don't do that again.
>
> The ocp_convert and cxx_eval_outermost_constant_expr hunks deal with making
> sure that a constant CONSTRUCTOR has the right type.
>
> Tested x86_64-pc-linux-gnu, applying to trunk.
>
>         * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
>         * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
>         (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
>         (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
>         right type.  Don't wrap a CONSTRUCTOR if one was passed in.
>         * tree.c (build_aggr_init_expr): Check for void.
> ---
>  gcc/cp/constexpr.c | 22 +++++++++++++---------
>  gcc/cp/cvt.c       | 10 +++++++++-
>  gcc/cp/tree.c      |  2 ++
>  gcc/cp/ChangeLog   |  7 +++++++
>  4 files changed, 31 insertions(+), 10 deletions(-)
>
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index 7692b1727da..4fb1ba527e3 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -2778,8 +2778,10 @@ initialized_type (tree t)
>  {
>    if (TYPE_P (t))
>      return t;
> -  tree type = cv_unqualified (TREE_TYPE (t));
> -  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
> +  tree type = TREE_TYPE (t);
> +  if (!VOID_TYPE_P (type))
> +    /* No need to look deeper.  */;
> +  else if (TREE_CODE (t) == CALL_EXPR)
>      {
>        /* A constructor call has void type, so we need to look deeper.  */
>        tree fn = get_function_named_in_call (t);
> @@ -2787,7 +2789,9 @@ initialized_type (tree t)
>           && DECL_CXX_CONSTRUCTOR_P (fn))
>         type = DECL_CONTEXT (fn);
>      }
> -  return type;
> +  else if (TREE_CODE (t) == AGGR_INIT_EXPR)
> +    type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
> +  return cv_unqualified (type);
>  }
>
>  /* We're about to initialize element INDEX of an array or class from VALUE.
> @@ -3000,7 +3004,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
>                                         &argvec, elttype, LOOKUP_NORMAL,
>                                         complain);
>        release_tree_vector (argvec);
> -      init = build_aggr_init_expr (TREE_TYPE (init), init);
> +      init = build_aggr_init_expr (elttype, init);
>        pre_init = true;
>      }
>
> @@ -5089,7 +5093,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
>         r = build_nop (TREE_TYPE (r), r);
>        TREE_CONSTANT (r) = false;
>      }
> -  else if (non_constant_p || r == t)
> +  else if (non_constant_p)
>      return t;
>
>    if (should_unshare)
> @@ -5097,18 +5101,18 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
>
>    if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
>      {
> +      r = adjust_temp_type (type, r);
>        if (TREE_CODE (t) == TARGET_EXPR
>           && TARGET_EXPR_INITIAL (t) == r)
>         return t;
> -      else
> +      else if (TREE_CODE (t) != CONSTRUCTOR)
>         {
>           r = get_target_expr (r);
>           TREE_CONSTANT (r) = true;
> -         return r;
>         }
>      }
> -  else
> -    return r;
> +
> +  return r;
>  }
>
>  /* Returns true if T is a valid subexpression of a constant expression,
> diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
> index 315b0d6a65a..b04e9a70652 100644
> --- a/gcc/cp/cvt.c
> +++ b/gcc/cp/cvt.c
> @@ -725,7 +725,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
>      /* We need a new temporary; don't take this shortcut. */;
>    else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
>      {
> -      if (same_type_p (type, TREE_TYPE (e)))
> +      tree etype = TREE_TYPE (e);
> +      if (same_type_p (type, etype))
>         /* The call to fold will not always remove the NOP_EXPR as
>            might be expected, since if one of the types is a typedef;
>            the comparison in fold is just equality of pointers, not a
> @@ -743,9 +744,16 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
>         {
>           /* Don't build a NOP_EXPR of class type.  Instead, change the
>              type of the temporary.  */
> +         gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
>           TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
>           return e;
>         }
> +      else if (TREE_CODE (e) == CONSTRUCTOR)
> +       {
> +         gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
> +         TREE_TYPE (e) = type;
> +         return e;
> +       }
>        else
>         {
>           /* We shouldn't be treating objects of ADDRESSABLE type as
> diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> index 74018e97bb7..51af9f2015e 100644
> --- a/gcc/cp/tree.c
> +++ b/gcc/cp/tree.c
> @@ -576,6 +576,8 @@ build_aggr_init_expr (tree type, tree init)
>    tree rval;
>    int is_ctor;
>
> +  gcc_assert (!VOID_TYPE_P (type));
> +
>    /* Don't build AGGR_INIT_EXPR in a template.  */
>    if (processing_template_decl)
>      return init;
> diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
> index 7e9c0e2642a..4f40627a226 100644
> --- a/gcc/cp/ChangeLog
> +++ b/gcc/cp/ChangeLog
> @@ -1,5 +1,12 @@
>  2018-11-04  Jason Merrill <[hidden email]><mailto:[hidden email]>
>
> +       * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
> +       * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
> +       (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
> +       (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
> +       right type.  Don't wrap a CONSTRUCTOR if one was passed in.
> +       * tree.c (build_aggr_init_expr): Check for void.
> +
>         PR c++/60503 - wrong lambda attribute syntax.
>         * parser.c (cp_parser_lambda_declarator_opt): Fix attribute
>         handling.
>
> base-commit: 703d2f69fad4772dc4aedb5327b5e3d88e8e1843
>

Reply | Threaded
Open this post in threaded view
|

Re: [C++ PATCH 1/3] Fix various latent issues revealed by P0732 work.

Christophe Lyon-2
On Fri, 9 Nov 2018 at 11:32, Kyrill Tkachov <[hidden email]> wrote:

>
>
> On 09/11/18 10:28, Matthew Malcomson wrote:
> > (Third attempt to put this on the mailing list now -- today is not a good day for my email skills :-[)
> >
> > Hi there,
> >
> > This patch has caused a few g++ and libstdc++ regression test failures on arm,
> >
> > I've included the g++ failures below.
> >
> > Do you mind looking into this?
> > Cheers,
> >
> > Matthew
> >
> >
> > dg-cmp-results.sh: Verbosity is 2, Variant is ""
> >
> > Older log file: just-before-problemcase-g++.sum
> > Test Run By matthew on Thu Nov  8 16:45:40 2018
> > Target is arm-none-eabi
> > Host   is x86_64-pc-linux-gnu
> >
> > Newer log file: g++-after-problem.sum
> > Test Run By matthew on Thu Nov  8 21:36:14 2018
> > Target is arm-none-eabi
> > Host   is x86_64-pc-linux-gnu
> >
> > PASS->FAIL: g++.dg/cpp0x/constexpr-static12.C  -std=c++14 scan-assembler-not _ZNSt10unique_ptrC1Ei
> > PASS->FAIL: g++.dg/cpp0x/constexpr-static12.C  -std=c++17 scan-assembler-not _ZNSt10unique_ptrC1Ei
> > PASS->FAIL: g++.dg/cpp0x/constexpr-static3.C -std=gnu++14 scan-assembler-not static_initialization
> > PASS->FAIL: g++.dg/cpp0x/constexpr-static3.C -std=gnu++17 scan-assembler-not static_initialization
> > PASS->FAIL: g++.dg/cpp0x/constexpr-static.C  -std=gnu++14 scan-assembler-not static_initialization
> > PASS->FAIL: g++.dg/cpp0x/constexpr-static.C  -std=gnu++17 scan-assembler-not static_initialization
> > PASS->FAIL: g++.dg/cpp0x/implicit13.C  -std=c++14 scan-assembler-not _ZN1BC1Ev
> > PASS->FAIL: g++.dg/cpp0x/implicit13.C  -std=c++17 scan-assembler-not _ZN1BC1Ev
> > NA->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++14 (internal compiler error)
> > PASS->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++14 (test for excess errors)
> > NA->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++17 (internal compiler error)
> > PASS->FAIL: g++.dg/cpp0x/pr83734.C  -std=gnu++17 (test for excess errors)
> > PASS->FAIL: g++.dg/cpp1y/constexpr-empty3.C  -std=c++14 scan-assembler-not static_init
> > PASS->FAIL: g++.dg/cpp1y/constexpr-empty3.C  -std=c++17 scan-assembler-not static_init
> >
> >
>
> I believe these are tracked in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87935 FWIW.
>

Yes, that was my intention.

> Kyrill
>
> > On 05/11/18 02:06, Jason Merrill wrote:
> > The initialized_type hunk fixes handling of void AGGR_INIT_EXPRs that call a
> > non-constructor; an AGGR_INIT_EXPR can have void type if its initialization
> > semantics are more complicated than just expanding the call.
> >
> > The cxx_eval_vec_init_1 hunk corrects AGGR_INIT_EXPRs that were
> > nonsensically built to initialize an object of void type. And the
> > build_aggr_init_expr hunk makes sure we don't do that again.
> >
> > The ocp_convert and cxx_eval_outermost_constant_expr hunks deal with making
> > sure that a constant CONSTRUCTOR has the right type.
> >
> > Tested x86_64-pc-linux-gnu, applying to trunk.
> >
> >     * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
> >     * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
> >     (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
> >     (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
> >     right type.  Don't wrap a CONSTRUCTOR if one was passed in.
> >     * tree.c (build_aggr_init_expr): Check for void.
> > ---
> >   gcc/cp/constexpr.c | 22 +++++++++++++---------
> >   gcc/cp/cvt.c       | 10 +++++++++-
> >   gcc/cp/tree.c      |  2 ++
> >   gcc/cp/ChangeLog   |  7 +++++++
> >   4 files changed, 31 insertions(+), 10 deletions(-)
> >
> > diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> > index 7692b1727da..4fb1ba527e3 100644
> > --- a/gcc/cp/constexpr.c
> > +++ b/gcc/cp/constexpr.c
> > @@ -2778,8 +2778,10 @@ initialized_type (tree t)
> >   {
> >     if (TYPE_P (t))
> >       return t;
> > -  tree type = cv_unqualified (TREE_TYPE (t));
> > -  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
> > +  tree type = TREE_TYPE (t);
> > +  if (!VOID_TYPE_P (type))
> > +    /* No need to look deeper.  */;
> > +  else if (TREE_CODE (t) == CALL_EXPR)
> >       {
> >         /* A constructor call has void type, so we need to look deeper.  */
> >         tree fn = get_function_named_in_call (t);
> > @@ -2787,7 +2789,9 @@ initialized_type (tree t)
> >         && DECL_CXX_CONSTRUCTOR_P (fn))
> >       type = DECL_CONTEXT (fn);
> >       }
> > -  return type;
> > +  else if (TREE_CODE (t) == AGGR_INIT_EXPR)
> > +    type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
> > +  return cv_unqualified (type);
> >   }
> >     /* We're about to initialize element INDEX of an array or class from VALUE.
> > @@ -3000,7 +3004,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
> >                       &argvec, elttype, LOOKUP_NORMAL,
> >                       complain);
> >         release_tree_vector (argvec);
> > -      init = build_aggr_init_expr (TREE_TYPE (init), init);
> > +      init = build_aggr_init_expr (elttype, init);
> >         pre_init = true;
> >       }
> >   @@ -5089,7 +5093,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
> >       r = build_nop (TREE_TYPE (r), r);
> >         TREE_CONSTANT (r) = false;
> >       }
> > -  else if (non_constant_p || r == t)
> > +  else if (non_constant_p)
> >       return t;
> >       if (should_unshare)
> > @@ -5097,18 +5101,18 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
> >       if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
> >       {
> > +      r = adjust_temp_type (type, r);
> >         if (TREE_CODE (t) == TARGET_EXPR
> >         && TARGET_EXPR_INITIAL (t) == r)
> >       return t;
> > -      else
> > +      else if (TREE_CODE (t) != CONSTRUCTOR)
> >       {
> >         r = get_target_expr (r);
> >         TREE_CONSTANT (r) = true;
> > -      return r;
> >       }
> >       }
> > -  else
> > -    return r;
> > +
> > +  return r;
> >   }
> >     /* Returns true if T is a valid subexpression of a constant expression,
> > diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
> > index 315b0d6a65a..b04e9a70652 100644
> > --- a/gcc/cp/cvt.c
> > +++ b/gcc/cp/cvt.c
> > @@ -725,7 +725,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
> >       /* We need a new temporary; don't take this shortcut. */;
> >     else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
> >       {
> > -      if (same_type_p (type, TREE_TYPE (e)))
> > +      tree etype = TREE_TYPE (e);
> > +      if (same_type_p (type, etype))
> >       /* The call to fold will not always remove the NOP_EXPR as
> >          might be expected, since if one of the types is a typedef;
> >          the comparison in fold is just equality of pointers, not a
> > @@ -743,9 +744,16 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
> >       {
> >         /* Don't build a NOP_EXPR of class type.  Instead, change the
> >            type of the temporary.  */
> > +      gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
> >         TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
> >         return e;
> >       }
> > +      else if (TREE_CODE (e) == CONSTRUCTOR)
> > +    {
> > +      gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
> > +      TREE_TYPE (e) = type;
> > +      return e;
> > +    }
> >         else
> >       {
> >         /* We shouldn't be treating objects of ADDRESSABLE type as
> > diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> > index 74018e97bb7..51af9f2015e 100644
> > --- a/gcc/cp/tree.c
> > +++ b/gcc/cp/tree.c
> > @@ -576,6 +576,8 @@ build_aggr_init_expr (tree type, tree init)
> >     tree rval;
> >     int is_ctor;
> >   +  gcc_assert (!VOID_TYPE_P (type));
> > +
> >     /* Don't build AGGR_INIT_EXPR in a template.  */
> >     if (processing_template_decl)
> >       return init;
> > diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
> > index 7e9c0e2642a..4f40627a226 100644
> > --- a/gcc/cp/ChangeLog
> > +++ b/gcc/cp/ChangeLog
> > @@ -1,5 +1,12 @@
> >   2018-11-04  Jason Merrill <[hidden email]><mailto:[hidden email]>
> >   +    * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
> > +    * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
> > +    (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
> > +    (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
> > +    right type.  Don't wrap a CONSTRUCTOR if one was passed in.
> > +    * tree.c (build_aggr_init_expr): Check for void.
> > +
> >       PR c++/60503 - wrong lambda attribute syntax.
> >       * parser.c (cp_parser_lambda_declarator_opt): Fix attribute
> >       handling.
> >
> > base-commit: 703d2f69fad4772dc4aedb5327b5e3d88e8e1843
> >
> >
> > On 05/11/18 02:06, Jason Merrill wrote:
> >
> > The initialized_type hunk fixes handling of void AGGR_INIT_EXPRs that call a
> > non-constructor; an AGGR_INIT_EXPR can have void type if its initialization
> > semantics are more complicated than just expanding the call.
> >
> > The cxx_eval_vec_init_1 hunk corrects AGGR_INIT_EXPRs that were
> > nonsensically built to initialize an object of void type. And the
> > build_aggr_init_expr hunk makes sure we don't do that again.
> >
> > The ocp_convert and cxx_eval_outermost_constant_expr hunks deal with making
> > sure that a constant CONSTRUCTOR has the right type.
> >
> > Tested x86_64-pc-linux-gnu, applying to trunk.
> >
> >         * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
> >         * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
> >         (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
> >         (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
> >         right type.  Don't wrap a CONSTRUCTOR if one was passed in.
> >         * tree.c (build_aggr_init_expr): Check for void.
> > ---
> >  gcc/cp/constexpr.c | 22 +++++++++++++---------
> >  gcc/cp/cvt.c       | 10 +++++++++-
> >  gcc/cp/tree.c      |  2 ++
> >  gcc/cp/ChangeLog   |  7 +++++++
> >  4 files changed, 31 insertions(+), 10 deletions(-)
> >
> > diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> > index 7692b1727da..4fb1ba527e3 100644
> > --- a/gcc/cp/constexpr.c
> > +++ b/gcc/cp/constexpr.c
> > @@ -2778,8 +2778,10 @@ initialized_type (tree t)
> >  {
> >    if (TYPE_P (t))
> >      return t;
> > -  tree type = cv_unqualified (TREE_TYPE (t));
> > -  if (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)
> > +  tree type = TREE_TYPE (t);
> > +  if (!VOID_TYPE_P (type))
> > +    /* No need to look deeper.  */;
> > +  else if (TREE_CODE (t) == CALL_EXPR)
> >      {
> >        /* A constructor call has void type, so we need to look deeper.  */
> >        tree fn = get_function_named_in_call (t);
> > @@ -2787,7 +2789,9 @@ initialized_type (tree t)
> >           && DECL_CXX_CONSTRUCTOR_P (fn))
> >         type = DECL_CONTEXT (fn);
> >      }
> > -  return type;
> > +  else if (TREE_CODE (t) == AGGR_INIT_EXPR)
> > +    type = TREE_TYPE (AGGR_INIT_EXPR_SLOT (t));
> > +  return cv_unqualified (type);
> >  }
> >
> >  /* We're about to initialize element INDEX of an array or class from VALUE.
> > @@ -3000,7 +3004,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init,
> >                                         &argvec, elttype, LOOKUP_NORMAL,
> >                                         complain);
> >        release_tree_vector (argvec);
> > -      init = build_aggr_init_expr (TREE_TYPE (init), init);
> > +      init = build_aggr_init_expr (elttype, init);
> >        pre_init = true;
> >      }
> >
> > @@ -5089,7 +5093,7 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
> >         r = build_nop (TREE_TYPE (r), r);
> >        TREE_CONSTANT (r) = false;
> >      }
> > -  else if (non_constant_p || r == t)
> > +  else if (non_constant_p)
> >      return t;
> >
> >    if (should_unshare)
> > @@ -5097,18 +5101,18 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
> >
> >    if (TREE_CODE (r) == CONSTRUCTOR && CLASS_TYPE_P (TREE_TYPE (r)))
> >      {
> > +      r = adjust_temp_type (type, r);
> >        if (TREE_CODE (t) == TARGET_EXPR
> >           && TARGET_EXPR_INITIAL (t) == r)
> >         return t;
> > -      else
> > +      else if (TREE_CODE (t) != CONSTRUCTOR)
> >         {
> >           r = get_target_expr (r);
> >           TREE_CONSTANT (r) = true;
> > -         return r;
> >         }
> >      }
> > -  else
> > -    return r;
> > +
> > +  return r;
> >  }
> >
> >  /* Returns true if T is a valid subexpression of a constant expression,
> > diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
> > index 315b0d6a65a..b04e9a70652 100644
> > --- a/gcc/cp/cvt.c
> > +++ b/gcc/cp/cvt.c
> > @@ -725,7 +725,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
> >      /* We need a new temporary; don't take this shortcut. */;
> >    else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
> >      {
> > -      if (same_type_p (type, TREE_TYPE (e)))
> > +      tree etype = TREE_TYPE (e);
> > +      if (same_type_p (type, etype))
> >         /* The call to fold will not always remove the NOP_EXPR as
> >            might be expected, since if one of the types is a typedef;
> >            the comparison in fold is just equality of pointers, not a
> > @@ -743,9 +744,16 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
> >         {
> >           /* Don't build a NOP_EXPR of class type.  Instead, change the
> >              type of the temporary.  */
> > +         gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
> >           TREE_TYPE (e) = TREE_TYPE (TARGET_EXPR_SLOT (e)) = type;
> >           return e;
> >         }
> > +      else if (TREE_CODE (e) == CONSTRUCTOR)
> > +       {
> > +         gcc_assert (same_type_ignoring_top_level_qualifiers_p (type, etype));
> > +         TREE_TYPE (e) = type;
> > +         return e;
> > +       }
> >        else
> >         {
> >           /* We shouldn't be treating objects of ADDRESSABLE type as
> > diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
> > index 74018e97bb7..51af9f2015e 100644
> > --- a/gcc/cp/tree.c
> > +++ b/gcc/cp/tree.c
> > @@ -576,6 +576,8 @@ build_aggr_init_expr (tree type, tree init)
> >    tree rval;
> >    int is_ctor;
> >
> > +  gcc_assert (!VOID_TYPE_P (type));
> > +
> >    /* Don't build AGGR_INIT_EXPR in a template.  */
> >    if (processing_template_decl)
> >      return init;
> > diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
> > index 7e9c0e2642a..4f40627a226 100644
> > --- a/gcc/cp/ChangeLog
> > +++ b/gcc/cp/ChangeLog
> > @@ -1,5 +1,12 @@
> >  2018-11-04  Jason Merrill <[hidden email]><mailto:[hidden email]>
> >
> > +       * cvt.c (ocp_convert): Don't wrap a CONSTRUCTOR in a NOP_EXPR.
> > +       * constexpr.c (initialized_type): Fix AGGR_INIT_EXPR handling.
> > +       (cxx_eval_vec_init_1): Correct type of AGGR_INIT_EXPR.
> > +       (cxx_eval_outermost_constant_expr): Make sure a CONSTRUCTOR has the
> > +       right type.  Don't wrap a CONSTRUCTOR if one was passed in.
> > +       * tree.c (build_aggr_init_expr): Check for void.
> > +
> >         PR c++/60503 - wrong lambda attribute syntax.
> >         * parser.c (cp_parser_lambda_declarator_opt): Fix attribute
> >         handling.
> >
> > base-commit: 703d2f69fad4772dc4aedb5327b5e3d88e8e1843
> >
>