RFA (libstdc++): C++ PATCH to implement C++17 noexcept in type system

Jason Merrill jason@redhat.com
Mon Nov 7 22:49:00 GMT 2016


This patch implements P0012R1: "Make exception-specifications be part
of the type system, version 5".  Some of the conversion logic was
already there from the transactional memory TS implementation, and
indeed the semantics of the noexcept function pointer conversion was
modeled on that wording, but I've simplified it in some places and
added some missing cases.

One thing that turned up was that we can't defer instantiation of
noexcept-specifiers outside of the toplevel type of a function, so I
needed to add a tsubst flag to indicate that context.

This patch implements Richard Smith's suggestion that we allow
noexcept(E) to be a deduced context in order to avoid another factor
of two expansion in the partial specializations of is_function.  This
is not part of the C++17 CD, but seems like the direction of the
committee.  Accordingly, I've added macros for this to c++config and
adjusted <type_traits> and <new> to use them.

Tested x86_64-pc-linux-gnu.  Are the libstdc++ changes OK for trunk?
-------------- next part --------------
commit 9291bd1bb7ffdc08586376df023363af0f5ee34e
Author: Jason Merrill <jason@redhat.com>
Date:   Fri Nov 4 13:19:25 2016 -0400

    Implement P0012R1, Make exception specifications part of the type system.
    
    gcc/cp/
            * cp-tree.h (enum tsubst_flags): Add tf_fndecl_type.
            (flag_noexcept_type, ce_type): New.
            * call.c (build_conv): Add ck_fnptr.
            (enum conversion_kind): Change ck_tsafe to ck_fnptr.
            (convert_like_real): Likewise.
            (standard_conversion): Likewise.  Allow function pointer
            conversions for pointers to member functions.
            (reference_compatible_p): Allow function pointer conversions.
            (direct_reference_binding): Likewise.
            (reference_binding): Reference-compatible is no longer a subset of
            reference-related.
            (is_subseq): Also strip ck_lvalue after next_conversion.
            * class.c (instantiate_type): Check fnptr_conv_p.
            (resolve_address_of_overloaded_function): Likewise.
            * cvt.c (can_convert_tx_safety): Now static.
            (noexcept_conv_p, fnptr_conv_p, strip_fnptr_conv): New.
            * decl.c (flag_noexcept_type): Define.
            (cxx_init_decl_processing): Set it.
            (bad_specifiers): Check it.
            (grokdeclarator) [cdk_function]: Add exception-spec to type here.
            * lambda.c (maybe_add_lambda_conv_op): Add exception-spec to
            returned pointer.
            * mangle.c (struct globals): Add need_cxx1z_warning.
            (mangle_decl): Check it.
            (write_exception_spec): New.
            (write_function_type): Call it.
            (canonicalize_for_substitution): Handle exception spec.
            (write_type): Likewise.
            (write_encoding): Set processing_template_decl across mangling of
            partially-instantiated type.
            * pt.c (determine_specialization): Pass tf_fndecl_type.
            (tsubst_decl, fn_type_unification): Likewise.
            (tsubst): Strip tf_fndecl_type, pass it to
            tsubst_exception_specification.
            (convert_nontype_argument_function): Handle function pointer
            conversion.
            (convert_nontype_argument): Likewise.
            (unify, for_each_template_parm_r): Walk into noexcept-specifier.
            * rtti.c (ptr_initializer): Encode noexcept.
            * tree.c (canonical_eh_spec): New.
            (build_exception_variant): Use it.
            * typeck.c (composite_pointer_type): Handle fnptr conversion.
            (comp_except_specs): Compare canonical EH specs.
            (structural_comptypes): Call it.
    gcc/c-family/
            * c.opt (Wc++1z-compat): New.
            * c-cppbuiltin.c (c_cpp_builtins): Add __cpp_noexcept_function_type.
    libstdc++-v3/
            * include/bits/c++config (_GLIBCXX_NOEXCEPT_PARM)
            (_GLIBCXX_NOEXCEPT_QUAL): New.
            * include/std/type_traits (is_function): Use them.
            * libsubc++/new (launder): Likewise.
            * libsupc++/cxxabi.h (__pbase_type_info::__masks): Add
            __noexcept_mask.
            * libsupc++/pbase_type_info.cc (__do_catch): Handle function
            pointer conversion.
    libiberty/
            * cp-demangle.c (is_fnqual_component_type): New.
            (d_encoding, d_print_comp_inner, d_print_mod_list): Use it.
            (FNQUAL_COMPONENT_CASE): New.
            (d_make_comp, has_return_type, d_print_comp_inner)
            (d_print_function_type): Use it.
            (next_is_type_qual): New.
            (d_cv_qualifiers, d_print_mod): Handle noexcept and throw-spec.
    include/
            * demangle.h (enum demangle_component_type): Add
            DEMANGLE_COMPONENT_NOEXCEPT, DEMANGLE_COMPONENT_THROW_SPEC.

diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c
index e40fa6f..55dbf44 100644
--- a/gcc/c-family/c-cppbuiltin.c
+++ b/gcc/c-family/c-cppbuiltin.c
@@ -941,6 +941,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_inline_variables=201606");
 	  cpp_define (pfile, "__cpp_aggregate_bases=201603");
 	  cpp_define (pfile, "__cpp_deduction_guides=201606");
+	  cpp_define (pfile, "__cpp_noexcept_function_type=201510");
 	}
       if (flag_concepts)
 	cpp_define (pfile, "__cpp_concepts=201507");
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 7d8a726..213353b 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -360,6 +360,13 @@ Wc++14-compat
 C++ ObjC++ Var(warn_cxx14_compat) Warning LangEnabledBy(C++ ObjC++,Wall)
 Warn about C++ constructs whose meaning differs between ISO C++ 2011 and ISO C++ 2014.
 
+Wc++1z-compat
+C++ ObjC++ Var(warn_cxx1z_compat) Warning LangEnabledBy(C++ ObjC++,Wall)
+Warn about C++ constructs whose meaning differs between ISO C++ 2014 and (forthcoming) ISO C++ 201z(7?).
+
+Wc++17-compat
+C++ ObjC++ Warning Alias(Wc++1z-compat) Undocumented
+
 Wcast-qual
 C ObjC C++ ObjC++ Var(warn_cast_qual) Warning
 Warn about casts which discard qualifiers.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 0466cd1..0dcf322 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -45,7 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 enum conversion_kind {
   ck_identity,
   ck_lvalue,
-  ck_tsafe,
+  ck_fnptr,
   ck_qual,
   ck_std,
   ck_ptr,
@@ -771,6 +771,7 @@ build_conv (conversion_kind code, tree type, conversion *from)
       break;
 
     case ck_qual:
+    case ck_fnptr:
       if (rank < cr_exact)
 	rank = cr_exact;
       break;
@@ -1285,18 +1286,6 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
 	  conv = build_conv (ck_ptr, from, conv);
 	  conv->base_p = true;
 	}
-      else if (tx_safe_fn_type_p (from_pointee))
-	{
-	  /* A prvalue of type "pointer to transaction_safe function" can be
-	     converted to a prvalue of type "pointer to function". */
-	  tree unsafe = tx_unsafe_fn_variant (from_pointee);
-	  if (same_type_p (unsafe, to_pointee))
-	    {
-	      from_pointee = unsafe;
-	      from = build_pointer_type (unsafe);
-	      conv = build_conv (ck_tsafe, from, conv);
-	    }
-	}
 
       if (same_type_p (from, to))
 	/* OK */;
@@ -1310,6 +1299,8 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
       else if (expr && string_conv_p (to, expr, 0))
 	/* converting from string constant to char *.  */
 	conv = build_conv (ck_qual, to, conv);
+      else if (fnptr_conv_p (to, from))
+	conv = build_conv (ck_fnptr, to, conv);
       /* Allow conversions among compatible ObjC pointer types (base
 	 conversions have been already handled above).  */
       else if (c_dialect_objc ()
@@ -1332,18 +1323,29 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
       tree fbase = class_of_this_parm (fromfn);
       tree tbase = class_of_this_parm (tofn);
 
-      if (!DERIVED_FROM_P (fbase, tbase)
-	  || !same_type_p (static_fn_type (fromfn),
-			   static_fn_type (tofn)))
+      if (!DERIVED_FROM_P (fbase, tbase))
+	return NULL;
+
+      tree fstat = static_fn_type (fromfn);
+      tree tstat = static_fn_type (tofn);
+      if (same_type_p (tstat, fstat)
+	  || fnptr_conv_p (tstat, fstat))
+	/* OK */;
+      else
 	return NULL;
 
-      from = build_memfn_type (fromfn,
-                               tbase,
-                               cp_type_quals (tbase),
-                               type_memfn_rqual (tofn));
-      from = build_ptrmemfunc_type (build_pointer_type (from));
-      conv = build_conv (ck_pmem, from, conv);
-      conv->base_p = true;
+      if (!same_type_p (fbase, tbase))
+	{
+	  from = build_memfn_type (fstat,
+				   tbase,
+				   cp_type_quals (tbase),
+				   type_memfn_rqual (tofn));
+	  from = build_ptrmemfunc_type (build_pointer_type (from));
+	  conv = build_conv (ck_pmem, from, conv);
+	  conv->base_p = true;
+	}
+      if (fnptr_conv_p (tstat, fstat))
+	conv = build_conv (ck_fnptr, to, conv);
     }
   else if (tcode == BOOLEAN_TYPE)
     {
@@ -1441,10 +1443,14 @@ reference_compatible_p (tree t1, tree t2)
 {
   /* [dcl.init.ref]
 
-     "cv1 T1" is reference compatible with "cv2 T2" if T1 is
-     reference-related to T2 and cv1 is the same cv-qualification as,
-     or greater cv-qualification than, cv2.  */
-  return (reference_related_p (t1, t2)
+     "cv1 T1" is reference compatible with "cv2 T2" if
+       * T1 is reference-related to T2 or
+       * T2 is "noexcept function" and T1 is "function", where the
+         function types are otherwise the same,
+     and cv1 is the same cv-qualification as, or greater cv-qualification
+     than, cv2.  */
+  return ((reference_related_p (t1, t2)
+	   || fnptr_conv_p (t1, t2))
 	  && at_least_as_qualified_p (t1, t2));
 }
 
@@ -1478,7 +1484,7 @@ direct_reference_binding (tree type, conversion *conv)
      either an identity conversion or, if the conversion function
      returns an entity of a type that is a derived class of the
      parameter type, a derived-to-base conversion.  */
-  if (!same_type_ignoring_top_level_qualifiers_p (t, conv->type))
+  if (is_properly_derived_from (conv->type, t))
     {
       /* Represent the derived-to-base conversion.  */
       conv = build_conv (ck_base, t, conv);
@@ -1591,7 +1597,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
      [8.5.3/5 dcl.init.ref] is changed to also require direct bindings for
      const and rvalue references to rvalues of compatible class type.
      We should also do direct bindings for non-class xvalues.  */
-  if (related_p && gl_kind)
+  if ((related_p || compatible_p) && gl_kind)
     {
       /* [dcl.init.ref]
 
@@ -6978,9 +6984,9 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
     case ck_lvalue:
       return decay_conversion (expr, complain);
 
-    case ck_tsafe:
+    case ck_fnptr:
       /* ??? Should the address of a transaction-safe pointer point to the TM
-	 clone, and this conversion look up the primary function?  */
+        clone, and this conversion look up the primary function?  */
       return build_nop (totype, expr);
 
     case ck_qual:
@@ -8863,10 +8869,15 @@ is_subseq (conversion *ics1, conversion *ics2)
 
       ics2 = next_conversion (ics2);
 
+      while (ics2->kind == ck_rvalue
+	     || ics2->kind == ck_lvalue)
+	ics2 = next_conversion (ics2);
+
       if (ics2->kind == ics1->kind
 	  && same_type_p (ics2->type, ics1->type)
-	  && same_type_p (next_conversion (ics2)->type,
-			  next_conversion (ics1)->type))
+	  && (ics1->kind == ck_identity
+	      || same_type_p (next_conversion (ics2)->type,
+			      next_conversion (ics1)->type)))
 	return true;
     }
 }
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index c6b4ed6..5460ae5 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -8177,10 +8177,14 @@ resolve_address_of_overloaded_function (tree target_type,
 	  if (DECL_ANTICIPATED (fn))
 	    continue;
 
+	  /* In C++17 we need the noexcept-qualifier to compare types.  */
+	  if (flag_noexcept_type)
+	    maybe_instantiate_noexcept (fn);
+
 	  /* See if there's a match.  */
 	  tree fntype = static_fn_type (fn);
 	  if (same_type_p (target_fn_type, fntype)
-	      || can_convert_tx_safety (target_fn_type, fntype))
+	      || fnptr_conv_p (target_fn_type, fntype))
 	    matches = tree_cons (fn, NULL_TREE, matches);
 	}
     }
@@ -8257,10 +8261,14 @@ resolve_address_of_overloaded_function (tree target_type,
 	      require_deduced_type (instantiation);
 	    }
 
+	  /* In C++17 we need the noexcept-qualifier to compare types.  */
+	  if (flag_noexcept_type)
+	    maybe_instantiate_noexcept (instantiation);
+
 	  /* See if there's a match.  */
 	  tree fntype = static_fn_type (instantiation);
 	  if (same_type_p (target_fn_type, fntype)
-	      || can_convert_tx_safety (target_fn_type, fntype))
+	      || fnptr_conv_p (target_fn_type, fntype))
 	    matches = tree_cons (instantiation, fn, matches);
 	}
 
@@ -8424,6 +8432,8 @@ instantiate_type (tree lhstype, tree rhs, tsubst_flags_t complain)
       tree fntype = non_reference (lhstype);
       if (same_type_p (fntype, TREE_TYPE (rhs)))
 	return rhs;
+      if (fnptr_conv_p (fntype, TREE_TYPE (rhs)))
+	return rhs;
       if (flag_ms_extensions
 	  && TYPE_PTRMEMFUNC_P (fntype)
 	  && !TYPE_PTRMEMFUNC_P (TREE_TYPE (rhs)))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d3a5aeb..20b52ad 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4744,6 +4744,8 @@ enum tsubst_flags {
 				    for calls in decltype (5.2.2/11).  */
   tf_partial = 1 << 8,		 /* Doing initial explicit argument
 				    substitution in fn_type_unification.  */
+  tf_fndecl_type = 1 << 9,   /* Substituting the type of a function
+				declaration.  */
   /* Convenient substitution flags combinations.  */
   tf_warning_or_error = tf_warning | tf_error
 };
@@ -4949,6 +4951,10 @@ extern int at_eof;
 
 extern bool defer_mangling_aliases;
 
+/* True if noexcept is part of the type (i.e. in C++17).  */
+
+extern bool flag_noexcept_type;
+
 /* A list of namespace-scope objects which have constructors or
    destructors which reside in the global scope.  The decl is stored
    in the TREE_VALUE slot and the initializer is stored in the
@@ -5737,7 +5743,8 @@ extern tree type_promotes_to			(tree);
 extern tree perform_qualification_conversions	(tree, tree);
 extern bool tx_safe_fn_type_p			(tree);
 extern tree tx_unsafe_fn_variant		(tree);
-extern bool can_convert_tx_safety		(tree, tree);
+extern bool fnptr_conv_p			(tree, tree);
+extern tree strip_fnptr_conv			(tree);
 
 /* in name-lookup.c */
 extern tree pushdecl				(tree);
@@ -6577,6 +6584,7 @@ extern tree build_overload			(tree, tree);
 extern tree ovl_scope				(tree);
 extern const char *cxx_printable_name		(tree, int);
 extern const char *cxx_printable_name_translate	(tree, int);
+extern tree canonical_eh_spec			(tree);
 extern tree build_exception_variant		(tree, tree);
 extern tree bind_template_template_parm		(tree, tree);
 extern tree array_type_nelts_total		(tree);
@@ -6648,7 +6656,7 @@ extern tree complete_type			(tree);
 extern tree complete_type_or_else		(tree, tree);
 extern tree complete_type_or_maybe_complain	(tree, tree, tsubst_flags_t);
 extern int type_unknown_p			(const_tree);
-enum { ce_derived, ce_normal, ce_exact };
+enum { ce_derived, ce_type, ce_normal, ce_exact };
 extern bool comp_except_specs			(const_tree, const_tree, int);
 extern bool comptypes				(tree, tree, int);
 extern bool same_type_ignoring_top_level_qualifiers_p (tree, tree);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 2f5f15a..400566f 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1932,9 +1932,84 @@ tx_unsafe_fn_variant (tree t)
 /* Return true iff FROM can convert to TO by a transaction-safety
    conversion.  */
 
-bool
+static bool
 can_convert_tx_safety (tree to, tree from)
 {
   return (flag_tm && tx_safe_fn_type_p (from)
 	  && same_type_p (to, tx_unsafe_fn_variant (from)));
 }
+
+/* Return true iff FROM can convert to TO by dropping noexcept.  */
+
+static bool
+noexcept_conv_p (tree to, tree from)
+{
+  if (!flag_noexcept_type)
+    return false;
+
+  tree t = non_reference (to);
+  tree f = from;
+  if (TYPE_PTRMEMFUNC_P (t)
+      && TYPE_PTRMEMFUNC_P (f))
+    {
+      t = TYPE_PTRMEMFUNC_FN_TYPE (t);
+      f = TYPE_PTRMEMFUNC_FN_TYPE (f);
+    }
+  if (TREE_CODE (t) == POINTER_TYPE
+      && TREE_CODE (f) == POINTER_TYPE)
+    {
+      t = TREE_TYPE (t);
+      f = TREE_TYPE (f);
+    }
+  tree_code code = TREE_CODE (f);
+  if (TREE_CODE (t) != code)
+    return false;
+  if (code != FUNCTION_TYPE && code != METHOD_TYPE)
+    return false;
+  if (!type_throw_all_p (t)
+      || type_throw_all_p (f))
+    return false;
+  tree v = build_exception_variant (f, NULL_TREE);
+  return same_type_p (t, v);
+}
+
+/* Return true iff FROM can convert to TO by a function pointer conversion.  */
+
+bool
+fnptr_conv_p (tree to, tree from)
+{
+  tree t = non_reference (to);
+  tree f = from;
+  if (TYPE_PTRMEMFUNC_P (t)
+      && TYPE_PTRMEMFUNC_P (f))
+    {
+      t = TYPE_PTRMEMFUNC_FN_TYPE (t);
+      f = TYPE_PTRMEMFUNC_FN_TYPE (f);
+    }
+  if (TREE_CODE (t) == POINTER_TYPE
+      && TREE_CODE (f) == POINTER_TYPE)
+    {
+      t = TREE_TYPE (t);
+      f = TREE_TYPE (f);
+    }
+
+  return (noexcept_conv_p (t, f)
+	  || can_convert_tx_safety (t, f));
+}
+
+/* Return FN with any NOP_EXPRs that represent function pointer
+   conversions stripped.  */
+
+tree
+strip_fnptr_conv (tree fn)
+{
+  while (TREE_CODE (fn) == NOP_EXPR)
+    {
+      tree op = TREE_OPERAND (fn, 0);
+      if (fnptr_conv_p (TREE_TYPE (fn), TREE_TYPE (op)))
+	fn = op;
+      else
+	break;
+    }
+  return fn;
+}
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index ecf4d14..c0321f9 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -73,8 +73,6 @@ static int check_static_variable_definition (tree, tree);
 static void record_unknown_type (tree, const char *);
 static tree builtin_function_1 (tree, tree, bool);
 static int member_function_or_else (tree, tree, enum overload_flags);
-static void bad_specifiers (tree, enum bad_spec_place, int, int, int, int,
-			    int);
 static void check_for_uninitialized_const_var (tree);
 static tree local_variable_p_walkfn (tree *, int *, void *);
 static const char *tag_name (enum tag_types);
@@ -227,6 +225,9 @@ struct GTY((for_user)) named_label_entry {
    function, two inside the body of a function in a local class, etc.)  */
 int function_depth;
 
+/* Whether the exception-specifier is part of a function type (i.e. C++17).  */
+bool flag_noexcept_type;
+
 /* States indicating how grokdeclarator() should handle declspecs marked
    with __attribute__((deprecated)).  An object declared as
    __attribute__((deprecated)) suppresses warnings of uses of other
@@ -4044,6 +4045,8 @@ cxx_init_decl_processing (void)
   std_node = current_namespace;
   pop_namespace ();
 
+  flag_noexcept_type = (cxx_dialect >= cxx1z);
+
   c_common_nodes_and_builtins ();
 
   integer_two_node = build_int_cst (NULL_TREE, 2);
@@ -7842,6 +7845,7 @@ bad_specifiers (tree object,
   if (friendp)
     error ("%q+D declared as a friend", object);
   if (raises
+      && !flag_noexcept_type
       && (TREE_CODE (object) == TYPE_DECL
 	  || (!TYPE_PTRFN_P (TREE_TYPE (object))
 	      && !TYPE_REFFN_P (TREE_TYPE (object))
@@ -10477,6 +10481,9 @@ grokdeclarator (const cp_declarator *declarator,
 		 The optional attribute-specifier-seq appertains to
 		 the function type.  */
 	      decl_attributes (&type, attrs, 0);
+
+	    if (raises)
+	      type = build_exception_variant (type, raises);
 	  }
 	  break;
 
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index d4284bf..c48cd52 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -1029,6 +1029,9 @@ maybe_add_lambda_conv_op (tree type)
   tree stattype = build_function_type (fn_result, FUNCTION_ARG_CHAIN (callop));
   stattype = (cp_build_type_attribute_variant
 	      (stattype, TYPE_ATTRIBUTES (optype)));
+  if (flag_noexcept_type
+      && TYPE_NOTHROW_P (TREE_TYPE (callop)))
+    stattype = build_exception_variant (stattype, noexcept_true_spec);
 
   /* First build up the conversion op.  */
 
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index f3b2fe3..a354ec5 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -114,6 +114,9 @@ struct GTY(()) globals {
   /* True if the mangling will be different in a future version of the
      ABI.  */
   bool need_abi_warning;
+
+  /* True if the mangling will be different in C++17 mode.  */
+  bool need_cxx1z_warning;
 };
 
 static GTY (()) globals G;
@@ -344,6 +347,43 @@ dump_substitution_candidates (void)
     }
 }
 
+/* <exception-spec> ::=
+      Do  -- non-throwing exception specification
+      DO <expression> E  -- computed (instantiation-dependent) noexcept
+      Dw <type>* E  -- throw (types)  */
+
+static void
+write_exception_spec (tree spec)
+{
+
+  if (!spec || spec == noexcept_false_spec)
+    /* Nothing.  */
+    return;
+
+  if (!flag_noexcept_type)
+    {
+      G.need_cxx1z_warning = true;
+      return;
+    }
+
+  if (nothrow_spec_p (spec))
+    write_string ("Do");
+  else if (TREE_PURPOSE (spec))
+    {
+      gcc_assert (uses_template_parms (TREE_PURPOSE (spec)));
+      write_string ("DO");
+      write_expression (TREE_PURPOSE (spec));
+      write_char ('E');
+    }
+  else
+    {
+      write_string ("Dw");
+      for (tree t = spec; t; t = TREE_CHAIN (t))
+	write_type (TREE_VALUE (t));
+      write_char ('E');
+    }
+}
+
 /* Both decls and types can be substitution candidates, but sometimes
    they refer to the same thing.  For instance, a TYPE_DECL and
    RECORD_TYPE for the same class refer to the same thing, and should
@@ -375,7 +415,15 @@ canonicalize_for_substitution (tree node)
 					cp_type_quals (node));
       if (TREE_CODE (node) == FUNCTION_TYPE
 	  || TREE_CODE (node) == METHOD_TYPE)
-	node = build_ref_qualified_type (node, type_memfn_rqual (orig));
+	{
+	  node = build_ref_qualified_type (node, type_memfn_rqual (orig));
+	  tree r = canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (orig));
+	  if (flag_noexcept_type)
+	    node = build_exception_variant (node, r);
+	  else
+	    /* Set the warning flag if appropriate.  */
+	    write_exception_spec (r);
+	}
     }
   return node;
 }
@@ -777,9 +825,11 @@ write_encoding (const tree decl)
     {
       tree fn_type;
       tree d;
+      bool tmpl = decl_is_template_id (decl, NULL);
 
-      if (decl_is_template_id (decl, NULL))
+      if (tmpl)
 	{
+	  ++processing_template_decl;
 	  fn_type = get_mostly_instantiated_function_type (decl);
 	  /* FN_TYPE will not have parameter types for in-charge or
 	     VTT parameters.  Therefore, we pass NULL_TREE to
@@ -796,6 +846,9 @@ write_encoding (const tree decl)
       write_bare_function_type (fn_type,
 				mangle_return_type_p (decl),
 				d);
+
+      if (tmpl)
+	--processing_template_decl;
     }
 }
 
@@ -2064,7 +2117,11 @@ write_type (tree type)
       type = TYPE_MAIN_VARIANT (type);
       if (TREE_CODE (type) == FUNCTION_TYPE
 	  || TREE_CODE (type) == METHOD_TYPE)
-	type = build_ref_qualified_type (type, type_memfn_rqual (type_orig));
+	{
+	  type = build_ref_qualified_type (type, type_memfn_rqual (type_orig));
+	  type = build_exception_variant (type,
+					  TYPE_RAISES_EXCEPTIONS (type_orig));
+	}
 
       /* According to the C++ ABI, some library classes are passed the
 	 same as the scalar type of their single member and use the same
@@ -2589,6 +2646,8 @@ write_function_type (const tree type)
       write_CV_qualifiers_for_type (this_type);
     }
 
+  write_exception_spec (TYPE_RAISES_EXCEPTIONS (type));
+
   if (tx_safe_fn_type_p (type))
     write_string ("Dx");
 
@@ -3776,6 +3835,12 @@ mangle_decl (const tree decl)
     }
   SET_DECL_ASSEMBLER_NAME (decl, id);
 
+  if (G.need_cxx1z_warning)
+    warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wc__1z_compat,
+		"mangled name for %qD will change in C++17 because the "
+		"exception specification is part of a function type",
+		decl);
+
   if (id != DECL_NAME (decl)
       /* Don't do this for a fake symbol we aren't going to emit anyway.  */
       && TREE_CODE (decl) != TYPE_DECL
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 45965aa..3df71dd 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -2216,7 +2216,7 @@ determine_specialization (tree template_id,
             continue;
 
           // Then, try to form the new function type.
-	  insttype = tsubst (TREE_TYPE (fn), targs, tf_none, NULL_TREE);
+	  insttype = tsubst (TREE_TYPE (fn), targs, tf_fndecl_type, NULL_TREE);
 	  if (insttype == error_mark_node)
 	    continue;
 	  fn_arg_types
@@ -5876,7 +5876,7 @@ get_underlying_template (tree tmpl)
 }
 
 /* Subroutine of convert_nontype_argument. Converts EXPR to TYPE, which
-   must be a function or a pointer-to-function type, as specified
+   must be a reference-to-function or a pointer-to-function type, as specified
    in [temp.arg.nontype]: disambiguate EXPR if it is an overload set,
    and check that the resulting function has external linkage.  */
 
@@ -5892,7 +5892,7 @@ convert_nontype_argument_function (tree type, tree expr,
   if (fn == error_mark_node)
     return error_mark_node;
 
-  fn_no_ptr = fn;
+  fn_no_ptr = strip_fnptr_conv (fn);
   if (TREE_CODE (fn_no_ptr) == ADDR_EXPR)
     fn_no_ptr = TREE_OPERAND (fn_no_ptr, 0);
   if (BASELINK_P (fn_no_ptr))
@@ -6672,6 +6672,11 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	  && !check_valid_ptrmem_cst_expr (type, expr, complain))
 	return error_mark_node;
 
+      /* Repeated conversion can't deal with a conversion that turns PTRMEM_CST
+	 into a CONSTRUCTOR, so build up a new PTRMEM_CST instead.  */
+      if (fnptr_conv_p (type, TREE_TYPE (expr)))
+	expr = make_ptrmem_cst (type, PTRMEM_CST_MEMBER (expr));
+
       /* There is no way to disable standard conversions in
 	 resolve_address_of_overloaded_function (called by
 	 instantiate_type). It is possible that the call succeeded by
@@ -8861,6 +8866,13 @@ for_each_template_parm_r (tree *tp, int *walk_subtrees, void *d)
 	   want walk_tree walking into them itself.  */
 	*walk_subtrees = 0;
       }
+
+      if (flag_noexcept_type)
+	{
+	  tree spec = TYPE_RAISES_EXCEPTIONS (t);
+	  if (spec)
+	    WALK_SUBTREE (TREE_PURPOSE (spec));
+	}
       break;
 
     case TYPEOF_TYPE:
@@ -11932,7 +11944,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 	    member = 0;
 	    ctx = DECL_CONTEXT (t);
 	  }
-	type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+	type = tsubst (TREE_TYPE (t), args, complain|tf_fndecl_type, in_decl);
 	if (type == error_mark_node)
 	  RETURN (error_mark_node);
 
@@ -13015,6 +13027,9 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	}
     }
 
+  bool fndecl_type = (complain & tf_fndecl_type);
+  complain &= ~tf_fndecl_type;
+
   if (type
       && code != TYPENAME_TYPE
       && code != TEMPLATE_TYPE_PARM
@@ -13512,8 +13527,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	  return error_mark_node;
 
 	/* Substitute the exception specification.  */
-	specs = tsubst_exception_specification (t, args, complain,
-						in_decl, /*defer_ok*/true);
+	specs = tsubst_exception_specification (t, args, complain, in_decl,
+						/*defer_ok*/fndecl_type);
 	if (specs == error_mark_node)
 	  return error_mark_node;
 	if (specs)
@@ -17991,7 +18006,7 @@ fn_type_unification (tree fn,
 	 access path at this point.  */
       push_deferring_access_checks (dk_deferred);
       fntype = tsubst (TREE_TYPE (fn), explicit_targs,
-		       complain | tf_partial, NULL_TREE);
+		       complain | tf_partial | tf_fndecl_type, NULL_TREE);
       pop_deferring_access_checks ();
       input_location = loc;
       processing_template_decl -= incomplete;
@@ -20333,9 +20348,27 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 	  args[i] = TREE_VALUE (a);
 	nargs = i;
 
-	return type_unification_real (tparms, targs, TYPE_ARG_TYPES (parm),
-				      args, nargs, 1, DEDUCE_EXACT,
-				      LOOKUP_NORMAL, NULL, explain_p);
+	if (type_unification_real (tparms, targs, TYPE_ARG_TYPES (parm),
+				   args, nargs, 1, DEDUCE_EXACT,
+				   LOOKUP_NORMAL, NULL, explain_p))
+	  return 1;
+
+	if (flag_noexcept_type)
+	  {
+	    tree pspec = TYPE_RAISES_EXCEPTIONS (parm);
+	    tree aspec = canonical_eh_spec (TYPE_RAISES_EXCEPTIONS (arg));
+	    if (pspec == NULL_TREE) pspec = noexcept_false_spec;
+	    if (aspec == NULL_TREE) aspec = noexcept_false_spec;
+	    if (TREE_PURPOSE (pspec) && TREE_PURPOSE (aspec)
+		&& uses_template_parms (TREE_PURPOSE (pspec)))
+	      RECUR_AND_CHECK_FAILURE (tparms, targs, TREE_PURPOSE (pspec),
+				       TREE_PURPOSE (aspec),
+				       UNIFY_ALLOW_NONE, explain_p);
+	    else if (nothrow_spec_p (pspec) && !nothrow_spec_p (aspec))
+	      return unify_type_mismatch (explain_p, parm, arg);
+	  }
+
+	return 0;
       }
 
     case OFFSET_TYPE:
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index cfbc3d4..247a98f 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -985,6 +985,14 @@ ptr_initializer (tinfo_s *ti, tree target)
       flags |= 0x20;
       to = tx_unsafe_fn_variant (to);
     }
+  if (flag_noexcept_type
+      && (TREE_CODE (to) == FUNCTION_TYPE
+	  || TREE_CODE (to) == METHOD_TYPE)
+      && TYPE_NOTHROW_P (to))
+    {
+      flags |= 0x40;
+      to = build_exception_variant (to, NULL_TREE);
+    }
   CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, init);
   CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, build_int_cst (NULL_TREE, flags));
   CONSTRUCTOR_APPEND_ELT (v, NULL_TREE,
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 4dc6e22..7872dd2 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -2209,6 +2209,27 @@ cxx_printable_name_translate (tree decl, int v)
   return cxx_printable_name_internal (decl, v, true);
 }
 

+/* Return the canonical version of exception-specification RAISES for a C++17
+   function type, for use in type comparison and building TYPE_CANONICAL.  */
+
+tree
+canonical_eh_spec (tree raises)
+{
+  if (raises == NULL_TREE)
+    return raises;
+  else if (DEFERRED_NOEXCEPT_SPEC_P (raises)
+	   || uses_template_parms (raises)
+	   || uses_template_parms (TREE_PURPOSE (raises)))
+    /* Keep a dependent or deferred exception specification.  */
+    return raises;
+  else if (nothrow_spec_p (raises))
+    /* throw() -> noexcept.  */
+    return noexcept_true_spec;
+  else
+    /* For C++17 type matching, anything else -> nothing.  */
+    return NULL_TREE;
+}
+
 /* Build the FUNCTION_TYPE or METHOD_TYPE which may throw exceptions
    listed in RAISES.  */
 
@@ -2230,6 +2251,25 @@ build_exception_variant (tree type, tree raises)
   /* Need to build a new variant.  */
   v = build_variant_type_copy (type);
   TYPE_RAISES_EXCEPTIONS (v) = raises;
+
+  if (!flag_noexcept_type)
+    /* The exception-specification is not part of the canonical type.  */
+    return v;
+
+  /* Canonicalize the exception specification.  */
+  tree cr = canonical_eh_spec (raises);
+
+  if (TYPE_STRUCTURAL_EQUALITY_P (type))
+    /* Propagate structural equality. */
+    SET_TYPE_STRUCTURAL_EQUALITY (v);
+  else if (TYPE_CANONICAL (type) != type || cr != raises)
+    /* Build the underlying canonical type, since it is different
+       from TYPE. */
+    TYPE_CANONICAL (v) = build_exception_variant (TYPE_CANONICAL (type), cr);
+  else
+    /* T is its own canonical type. */
+    TYPE_CANONICAL (v) = v;
+
   return v;
 }
 
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 4ff2bc2..211696c 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -647,6 +647,14 @@ composite_pointer_type (tree t1, tree t2, tree arg1, tree arg2,
 	return objc_common_type (t1, t2);
     }
 
+  /* if T1 or T2 is "pointer to noexcept function" and the other type is
+     "pointer to function", where the function types are otherwise the same,
+     "pointer to function" */
+  if (fnptr_conv_p (t1, t2))
+    return t1;
+  if (fnptr_conv_p (t2, t1))
+    return t2;
+
   /* [expr.eq] permits the application of a pointer conversion to
      bring the pointers to a common type.  */
   if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2)
@@ -710,22 +718,6 @@ composite_pointer_type (tree t1, tree t2, tree arg1, tree arg2,
           return error_mark_node;
         }
     }
-  else if (TYPE_PTR_P (t1) && TYPE_PTR_P (t2)
-	   && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (t1))
-	   && TREE_CODE (TREE_TYPE (t2)) == TREE_CODE (TREE_TYPE (t1)))
-    {
-      /* ...if T1 is "pointer to transaction_safe function" and T2 is "pointer
-	 to function", where the function types are otherwise the same, T2, and
-	 vice versa.... */
-      tree f1 = TREE_TYPE (t1);
-      tree f2 = TREE_TYPE (t2);
-      bool safe1 = tx_safe_fn_type_p (f1);
-      bool safe2 = tx_safe_fn_type_p (f2);
-      if (safe1 && !safe2)
-	t1 = build_pointer_type (tx_unsafe_fn_variant (f1));
-      else if (safe2 && !safe1)
-	t2 = build_pointer_type (tx_unsafe_fn_variant (f2));
-    }
 
   return composite_pointer_type_r (t1, t2, operation, complain);
 }
@@ -1020,6 +1012,7 @@ comp_except_types (tree a, tree b, bool exact)
 
 /* Return true if TYPE1 and TYPE2 are equivalent exception specifiers.
    If EXACT is ce_derived, T2 can be stricter than T1 (according to 15.4/5).
+   If EXACT is ce_type, the C++17 type compatibility rules apply.
    If EXACT is ce_normal, the compatibility rules in 15.4/3 apply.
    If EXACT is ce_exact, the specs must be exactly the same. Exception lists
    are unordered, but we've already filtered out duplicates. Most lists will
@@ -1038,8 +1031,13 @@ comp_except_specs (const_tree t1, const_tree t2, int exact)
   /* First handle noexcept.  */
   if (exact < ce_exact)
     {
+      if (exact == ce_type
+	  && (canonical_eh_spec (CONST_CAST_TREE (t1))
+	      == canonical_eh_spec (CONST_CAST_TREE (t2))))
+	return true;
+
       /* noexcept(false) is compatible with no exception-specification,
-	 and stricter than any spec.  */
+	 and less strict than any spec.  */
       if (t1 == noexcept_false_spec)
 	return t2 == NULL_TREE || exact == ce_derived;
       /* Even a derived noexcept(false) is compatible with no
@@ -1222,10 +1220,17 @@ structural_comptypes (tree t1, tree t2, int strict)
     return false;
   /* Need to check this before TYPE_MAIN_VARIANT.
      FIXME function qualifiers should really change the main variant.  */
-  if ((TREE_CODE (t1) == FUNCTION_TYPE
-       || TREE_CODE (t1) == METHOD_TYPE)
-      && type_memfn_rqual (t1) != type_memfn_rqual (t2))
-    return false;
+  if (TREE_CODE (t1) == FUNCTION_TYPE
+      || TREE_CODE (t1) == METHOD_TYPE)
+    {
+      if (type_memfn_rqual (t1) != type_memfn_rqual (t2))
+	return false;
+      if (flag_noexcept_type
+	  && !comp_except_specs (TYPE_RAISES_EXCEPTIONS (t1),
+				 TYPE_RAISES_EXCEPTIONS (t2),
+				 ce_type))
+	return false;
+    }
 
   /* Allow for two different type nodes which have essentially the same
      definition.  Note that we already checked for equality of the type
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e0afc26..17c5c22 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -5574,6 +5574,11 @@ enabled by @option{-Wall}.
 Warn about C++ constructs whose meaning differs between ISO C++ 2011
 and ISO C++ 2014.  This warning is enabled by @option{-Wall}.
 
+@item -Wc++1z-compat @r{(C++ and Objective-C++ only)}
+@opindex Wc++1z-compat
+Warn about C++ constructs whose meaning differs between ISO C++ 2014
+and the forthoming ISO C++ 2017(?).  This warning is enabled by @option{-Wall}.
+
 @item -Wcast-qual
 @opindex Wcast-qual
 @opindex Wno-cast-qual
diff --git a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
index 6ac66e0..f4658a9 100644
--- a/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
+++ b/gcc/testsuite/g++.dg/cpp1z/feat-cxx1z.C
@@ -380,6 +380,12 @@
 #  error "__cpp_capture_star_this != 201603"
 #endif
 
+#ifndef __cpp_noexcept_function_type
+#  error "__cpp_noexcept_function_type"
+#elif __cpp_noexcept_function_type != 201510
+#  error "__cpp_noexcept_function_type != 201510"
+#endif
+
 #ifdef __has_cpp_attribute
 
 #  if ! __has_cpp_attribute(maybe_unused)
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type1.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type1.C
new file mode 100644
index 0000000..62e1322
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type1.C
@@ -0,0 +1,8 @@
+// Testcase from P0012r1
+// { dg-options -std=c++1z }
+
+void (*p)() throw(int);
+void (**pp)() noexcept = &p;   // { dg-error "" } cannot convert to pointer to noexcept function
+
+struct S { typedef void (*p)(); operator p(); };
+void (*q)() noexcept = S();   // { dg-error "" } cannot convert to pointer to noexcept function
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type11.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type11.C
new file mode 100644
index 0000000..bcd4d8d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type11.C
@@ -0,0 +1,3 @@
+// { dg-options "-Wall -std=c++14" }
+
+void f(int(*)() noexcept) { }	// { dg-warning "mangled" }
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type12.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type12.C
new file mode 100644
index 0000000..2e9fb8a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type12.C
@@ -0,0 +1,19 @@
+// { dg-options -std=c++1z }
+
+template <class R, class... A, bool B>
+void f(R (*)(A...) noexcept(B)) { }
+
+template <class R, class... A, bool B>
+void f2(R (*)(A...) noexcept(B)) { }
+
+void g(int);
+void h(int) noexcept;
+
+int main()
+{
+  f(g);
+  f2(h);
+}
+
+// { dg-final { scan-assembler "_Z1fIvJiELb0EEvPDOT1_EFT_DpT0_E" } }
+// { dg-final { scan-assembler "_Z2f2IvJiELb1EEvPDOT1_EFT_DpT0_E" } }
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type2.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type2.C
new file mode 100644
index 0000000..747bb19
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type2.C
@@ -0,0 +1,10 @@
+// Test for function pointer conversion on template arguments.
+// { dg-options -std=c++1z }
+
+template <void (*P)()> struct A { };
+
+void f() noexcept { };
+constexpr void (*p)() noexcept = f;
+
+A<f> a;
+A<p> b;
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type3.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type3.C
new file mode 100644
index 0000000..9303da8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type3.C
@@ -0,0 +1,13 @@
+// Test for overload resolution.
+// { dg-options -std=c++1z }
+
+void f(void (*)() noexcept) = delete;
+void f(void (*)()) { }
+void g() {}
+void h() noexcept {}
+
+int main()
+{
+  f(g);
+  f(h);				// { dg-error "deleted" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type4.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type4.C
new file mode 100644
index 0000000..621da93
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type4.C
@@ -0,0 +1,16 @@
+// Test for deduction.
+// { dg-options -std=c++1z }
+
+template <class R, class... A>
+void f(R (*)(A...));
+void g(int) noexcept;
+
+template <class R, class... A>
+void h(R (*)(A...) noexcept);
+void i(int);
+
+int main()
+{
+  f(g);
+  h(i);				// { dg-error "" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type5.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type5.C
new file mode 100644
index 0000000..9e5d202
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type5.C
@@ -0,0 +1,25 @@
+// Test for composite pointer type.
+// { dg-options -std=c++1z }
+
+typedef void (*P)();
+typedef void (*NP)() noexcept;
+
+void f();
+void g() noexcept;
+
+bool b;
+
+template <class T, class U> struct Same;
+template <class T> struct Same<T,T> { };
+
+Same<decltype(b ? &f : &g),P> s;
+
+int main()
+{
+  P p = 0;
+  NP np = 0;
+
+  p == np;
+  p != np;
+  p < np;
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type6.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type6.C
new file mode 100644
index 0000000..5068457
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type6.C
@@ -0,0 +1,13 @@
+// Test for lambda conversion.
+// { dg-options -std=c++1z }
+
+void f()
+{
+  auto l = []() noexcept { return 0; };
+  int (*p)() noexcept = l;
+  int (*q)() = l;
+
+  auto l2 = []{ return 0; };
+  p = l2;			// { dg-error "" }
+  q = l2;
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type7.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type7.C
new file mode 100644
index 0000000..1f78114
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type7.C
@@ -0,0 +1,14 @@
+// Test for static_cast.
+// { dg-options -std=c++1z }
+
+void f()
+{
+  typedef void (*P)();
+  typedef void (*NP)() noexcept;
+
+  P p;
+  NP np;
+
+  static_cast<P>(np);
+  static_cast<NP>(p);		// { dg-error "" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type8.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type8.C
new file mode 100644
index 0000000..0182e3a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type8.C
@@ -0,0 +1,22 @@
+// Test for exception handling.
+// { dg-options -std=c++1z }
+// { dg-do run }
+
+void f() {}
+void g() noexcept {}
+
+int main()
+{
+  try { throw g; }
+  catch (void (*)()) { }
+
+  try { throw g; }
+  catch (void (*)() noexcept) { }
+
+  try { throw f; }
+  catch (void (*)()) { }
+
+  try { throw f; }
+  catch (void (*)() noexcept) { __builtin_abort(); }
+  catch (...) { }
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/noexcept-type9.C b/gcc/testsuite/g++.dg/cpp1z/noexcept-type9.C
new file mode 100644
index 0000000..4547c4c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/noexcept-type9.C
@@ -0,0 +1,18 @@
+// Test for PMF template args.
+// { dg-options -std=c++1z }
+// { dg-do run }
+
+struct A
+{
+  void f() noexcept;
+  void g();
+};
+
+template <void (A::*)()> struct B { };
+template <void (A::*)() noexcept> struct C { };
+
+B<&A::f> b1;
+B<&A::g> b2;
+
+C<&A::f> c1;
+C<&A::g> c2;			// { dg-error "" }
diff --git a/gcc/testsuite/g++.dg/eh/spec2.C b/gcc/testsuite/g++.dg/eh/spec2.C
index 64807dd..8107f01 100644
--- a/gcc/testsuite/g++.dg/eh/spec2.C
+++ b/gcc/testsuite/g++.dg/eh/spec2.C
@@ -2,9 +2,9 @@
 
 struct S { void f (void); };
 
-typedef void f1 (void) throw (int); // { dg-error "exception" }
-typedef void (*f2) (void) throw (int); // { dg-error "exception" }
-typedef void (S::*f3) (void) throw (int); // { dg-error "exception" }
+typedef void f1 (void) throw (int); // { dg-error "exception" "" { target c++14_down } }
+typedef void (*f2) (void) throw (int); // { dg-error "exception" "" { target c++14_down } }
+typedef void (S::*f3) (void) throw (int); // { dg-error "exception" "" { target c++14_down } }
 
 void (*f4) (void) throw (int);
 void (S::*f5) (void) throw (int);
diff --git a/gcc/testsuite/g++.old-deja/g++.eh/spec7.C b/gcc/testsuite/g++.old-deja/g++.eh/spec7.C
index 2ef88a2..2a4a989 100644
--- a/gcc/testsuite/g++.old-deja/g++.eh/spec7.C
+++ b/gcc/testsuite/g++.old-deja/g++.eh/spec7.C
@@ -10,7 +10,7 @@ struct A
   static void (A::*pmf)() throw ();
 };
 
-void (A::* A::pmf)() = &A::g;
+void (A::* A::pmf)() throw() = &A::g;
 
 int main()
 {
diff --git a/include/demangle.h b/include/demangle.h
index 1d7cadf..7a03c20 100644
--- a/include/demangle.h
+++ b/include/demangle.h
@@ -449,7 +449,9 @@ enum demangle_component_type
   /* A transaction-safe function type.  */
   DEMANGLE_COMPONENT_TRANSACTION_SAFE,
   /* A cloned function.  */
-  DEMANGLE_COMPONENT_CLONE
+  DEMANGLE_COMPONENT_CLONE,
+  DEMANGLE_COMPONENT_NOEXCEPT,
+  DEMANGLE_COMPONENT_THROW_SPEC
 };
 
 /* Types which are only used internally.  */
diff --git a/libiberty/cp-demangle.c b/libiberty/cp-demangle.c
index 46382cc..e239155 100644
--- a/libiberty/cp-demangle.c
+++ b/libiberty/cp-demangle.c
@@ -436,6 +436,8 @@ static struct demangle_component *d_operator_name (struct d_info *);
 
 static struct demangle_component *d_special_name (struct d_info *);
 
+static struct demangle_component *d_parmlist (struct d_info *);
+
 static int d_call_offset (struct d_info *, int);
 
 static struct demangle_component *d_ctor_dtor_name (struct d_info *);
@@ -559,6 +561,32 @@ static int d_demangle_callback (const char *, int,
                                 demangle_callbackref, void *);
 static char *d_demangle (const char *, int, size_t *);
 
+/* True iff TYPE is a demangling component representing a
+   function-type-qualifier.  */
+
+static int
+is_fnqual_component_type (enum demangle_component_type type)
+{
+  return (type == DEMANGLE_COMPONENT_RESTRICT_THIS
+	  || type == DEMANGLE_COMPONENT_VOLATILE_THIS
+	  || type == DEMANGLE_COMPONENT_CONST_THIS
+	  || type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS
+	  || type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
+	  || type == DEMANGLE_COMPONENT_NOEXCEPT
+	  || type == DEMANGLE_COMPONENT_THROW_SPEC
+	  || type == DEMANGLE_COMPONENT_REFERENCE_THIS);
+}
+
+#define FNQUAL_COMPONENT_CASE				\
+    case DEMANGLE_COMPONENT_RESTRICT_THIS:		\
+    case DEMANGLE_COMPONENT_VOLATILE_THIS:		\
+    case DEMANGLE_COMPONENT_CONST_THIS:			\
+    case DEMANGLE_COMPONENT_REFERENCE_THIS:		\
+    case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:	\
+    case DEMANGLE_COMPONENT_TRANSACTION_SAFE:		\
+    case DEMANGLE_COMPONENT_NOEXCEPT:			\
+    case DEMANGLE_COMPONENT_THROW_SPEC
+
 #ifdef CP_DEMANGLE_DEBUG
 
 static void
@@ -984,14 +1012,9 @@ d_make_comp (struct d_info *di, enum demangle_component_type type,
     case DEMANGLE_COMPONENT_RESTRICT:
     case DEMANGLE_COMPONENT_VOLATILE:
     case DEMANGLE_COMPONENT_CONST:
-    case DEMANGLE_COMPONENT_RESTRICT_THIS:
-    case DEMANGLE_COMPONENT_VOLATILE_THIS:
-    case DEMANGLE_COMPONENT_CONST_THIS:
-    case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
-    case DEMANGLE_COMPONENT_REFERENCE_THIS:
-    case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
     case DEMANGLE_COMPONENT_ARGLIST:
     case DEMANGLE_COMPONENT_TEMPLATE_ARGLIST:
+    FNQUAL_COMPONENT_CASE:
       break;
 
       /* Other types should not be seen here.  */
@@ -1225,12 +1248,7 @@ has_return_type (struct demangle_component *dc)
       return 0;
     case DEMANGLE_COMPONENT_TEMPLATE:
       return ! is_ctor_dtor_or_conversion (d_left (dc));
-    case DEMANGLE_COMPONENT_RESTRICT_THIS:
-    case DEMANGLE_COMPONENT_VOLATILE_THIS:
-    case DEMANGLE_COMPONENT_CONST_THIS:
-    case DEMANGLE_COMPONENT_REFERENCE_THIS:
-    case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
-    case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
+    FNQUAL_COMPONENT_CASE:
       return has_return_type (d_left (dc));
     }
 }
@@ -1287,13 +1305,12 @@ d_encoding (struct d_info *di, int top_level)
 	  while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS
 		 || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS
 		 || dc->type == DEMANGLE_COMPONENT_CONST_THIS
-		 || dc->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
 		 || dc->type == DEMANGLE_COMPONENT_REFERENCE_THIS
 		 || dc->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)
 	    dc = d_left (dc);
 
 	  /* If the top level is a DEMANGLE_COMPONENT_LOCAL_NAME, then
-	     there may be CV-qualifiers on its right argument which
+	     there may be function-qualifiers on its right argument which
 	     really apply here; this happens when parsing a class
 	     which is local to a function.  */
 	  if (dc->type == DEMANGLE_COMPONENT_LOCAL_NAME)
@@ -1301,12 +1318,7 @@ d_encoding (struct d_info *di, int top_level)
 	      struct demangle_component *dcr;
 
 	      dcr = d_right (dc);
-	      while (dcr->type == DEMANGLE_COMPONENT_RESTRICT_THIS
-		     || dcr->type == DEMANGLE_COMPONENT_VOLATILE_THIS
-		     || dcr->type == DEMANGLE_COMPONENT_CONST_THIS
-		     || dcr->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
-		     || dcr->type == DEMANGLE_COMPONENT_REFERENCE_THIS
-		     || dcr->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)
+	      while (is_fnqual_component_type (dcr->type))
 		dcr = d_left (dcr);
 	      dc->u.s_binary.right = dcr;
 	    }
@@ -2239,6 +2251,24 @@ d_ctor_dtor_name (struct d_info *di)
     }
 }
 
+/* True iff we're looking at an order-insensitive type-qualifier, including
+   function-type-qualifiers.  */
+
+static int
+next_is_type_qual (struct d_info *di)
+{
+  char peek = d_peek_char (di);
+  if (peek == 'r' || peek == 'V' || peek == 'K')
+    return 1;
+  if (peek == 'D')
+    {
+      peek = d_peek_next_char (di);
+      if (peek == 'x' || peek == 'o' || peek == 'O' || peek == 'w')
+	return 1;
+    }
+  return 0;
+}
+
 /* <type> ::= <builtin-type>
           ::= <function-type>
           ::= <class-enum-type>
@@ -2324,9 +2354,7 @@ cplus_demangle_type (struct d_info *di)
      __vector, and it treats it as order-sensitive when mangling
      names.  */
 
-  peek = d_peek_char (di);
-  if (peek == 'r' || peek == 'V' || peek == 'K'
-      || (peek == 'D' && d_peek_next_char (di) == 'x'))
+  if (next_is_type_qual (di))
     {
       struct demangle_component **pret;
 
@@ -2361,6 +2389,7 @@ cplus_demangle_type (struct d_info *di)
 
   can_subst = 1;
 
+  peek = d_peek_char (di);
   switch (peek)
     {
     case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
@@ -2648,10 +2677,10 @@ d_cv_qualifiers (struct d_info *di,
 
   pstart = pret;
   peek = d_peek_char (di);
-  while (peek == 'r' || peek == 'V' || peek == 'K'
-	 || (peek == 'D' && d_peek_next_char (di) == 'x'))
+  while (next_is_type_qual (di))
     {
       enum demangle_component_type t;
+      struct demangle_component *right = NULL;
 
       d_advance (di, 1);
       if (peek == 'r')
@@ -2677,12 +2706,41 @@ d_cv_qualifiers (struct d_info *di,
 	}
       else
 	{
-	  t = DEMANGLE_COMPONENT_TRANSACTION_SAFE;
-	  di->expansion += sizeof "transaction_safe";
-	  d_advance (di, 1);
+	  peek = d_next_char (di);
+	  if (peek == 'x')
+	    {
+	      t = DEMANGLE_COMPONENT_TRANSACTION_SAFE;
+	      di->expansion += sizeof "transaction_safe";
+	    }
+	  else if (peek == 'o'
+		   || peek == 'O')
+	    {
+	      t = DEMANGLE_COMPONENT_NOEXCEPT;
+	      di->expansion += sizeof "noexcept";
+	      if (peek == 'O')
+		{
+		  right = d_expression (di);
+		  if (right == NULL)
+		    return NULL;
+		  if (! d_check_char (di, 'E'))
+		    return NULL;
+		}
+	    }
+	  else if (peek == 'w')
+	    {
+	      t = DEMANGLE_COMPONENT_THROW_SPEC;
+	      di->expansion += sizeof "throw";
+	      right = d_parmlist (di);
+	      if (right == NULL)
+		return NULL;
+	      if (! d_check_char (di, 'E'))
+		return NULL;
+	    }
+	  else
+	    return NULL;
 	}
 
-      *pret = d_make_comp (di, t, NULL, NULL);
+      *pret = d_make_comp (di, t, NULL, right);
       if (*pret == NULL)
 	return NULL;
       pret = &d_left (*pret);
@@ -3973,6 +4031,8 @@ d_count_templates_scopes (int *num_templates, int *num_scopes,
     case DEMANGLE_COMPONENT_REFERENCE_THIS:
     case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
     case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
+    case DEMANGLE_COMPONENT_NOEXCEPT:
+    case DEMANGLE_COMPONENT_THROW_SPEC:
     case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
     case DEMANGLE_COMPONENT_POINTER:
     case DEMANGLE_COMPONENT_COMPLEX:
@@ -4587,12 +4647,7 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
 	    adpm[i].templates = dpi->templates;
 	    ++i;
 
-	    if (typed_name->type != DEMANGLE_COMPONENT_RESTRICT_THIS
-		&& typed_name->type != DEMANGLE_COMPONENT_VOLATILE_THIS
-		&& typed_name->type != DEMANGLE_COMPONENT_CONST_THIS
-		&& typed_name->type != DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS
-		&& typed_name->type != DEMANGLE_COMPONENT_TRANSACTION_SAFE
-		&& typed_name->type != DEMANGLE_COMPONENT_REFERENCE_THIS)
+	    if (!is_fnqual_component_type (typed_name->type))
 	      break;
 
 	    typed_name = d_left (typed_name);
@@ -4629,13 +4684,7 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
 		d_print_error (dpi);
 		return;
 	      }
-	    while (local_name->type == DEMANGLE_COMPONENT_RESTRICT_THIS
-		   || local_name->type == DEMANGLE_COMPONENT_VOLATILE_THIS
-		   || local_name->type == DEMANGLE_COMPONENT_CONST_THIS
-		   || local_name->type == DEMANGLE_COMPONENT_REFERENCE_THIS
-		   || local_name->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
-		   || (local_name->type
-		       == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS))
+	    while (is_fnqual_component_type (local_name->type))
 	      {
 		if (i >= sizeof adpm / sizeof adpm[0])
 		  {
@@ -4960,16 +5009,11 @@ d_print_comp_inner (struct d_print_info *dpi, int options,
       }
       /* Fall through.  */
 
-    case DEMANGLE_COMPONENT_RESTRICT_THIS:
-    case DEMANGLE_COMPONENT_VOLATILE_THIS:
-    case DEMANGLE_COMPONENT_CONST_THIS:
-    case DEMANGLE_COMPONENT_REFERENCE_THIS:
-    case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
     case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
     case DEMANGLE_COMPONENT_POINTER:
     case DEMANGLE_COMPONENT_COMPLEX:
     case DEMANGLE_COMPONENT_IMAGINARY:
-    case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
+    FNQUAL_COMPONENT_CASE:
     modifier:
       {
 	/* We keep a list of modifiers on the stack.  */
@@ -5674,13 +5718,7 @@ d_print_mod_list (struct d_print_info *dpi, int options,
 
   if (mods->printed
       || (! suffix
-	  && (mods->mod->type == DEMANGLE_COMPONENT_RESTRICT_THIS
-	      || mods->mod->type == DEMANGLE_COMPONENT_VOLATILE_THIS
-	      || mods->mod->type == DEMANGLE_COMPONENT_CONST_THIS
-	      || mods->mod->type == DEMANGLE_COMPONENT_REFERENCE_THIS
-	      || mods->mod->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
-	      || (mods->mod->type
-		  == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS))))
+	  && (is_fnqual_component_type (mods->mod->type))))
     {
       d_print_mod_list (dpi, options, mods->next, suffix);
       return;
@@ -5733,12 +5771,7 @@ d_print_mod_list (struct d_print_info *dpi, int options,
 	  dc = dc->u.s_unary_num.sub;
 	}
 
-      while (dc->type == DEMANGLE_COMPONENT_RESTRICT_THIS
-	     || dc->type == DEMANGLE_COMPONENT_VOLATILE_THIS
-	     || dc->type == DEMANGLE_COMPONENT_CONST_THIS
-	     || dc->type == DEMANGLE_COMPONENT_REFERENCE_THIS
-	     || dc->type == DEMANGLE_COMPONENT_TRANSACTION_SAFE
-	     || dc->type == DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS)
+      while (is_fnqual_component_type (dc->type))
 	dc = d_left (dc);
 
       d_print_comp (dpi, options, dc);
@@ -5777,6 +5810,24 @@ d_print_mod (struct d_print_info *dpi, int options,
     case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
       d_append_string (dpi, " transaction_safe");
       return;
+    case DEMANGLE_COMPONENT_NOEXCEPT:
+      d_append_string (dpi, " noexcept");
+      if (d_right (mod))
+	{
+	  d_append_char (dpi, '(');
+	  d_print_comp (dpi, options, d_right (mod));
+	  d_append_char (dpi, ')');
+	}
+      return;
+    case DEMANGLE_COMPONENT_THROW_SPEC:
+      d_append_string (dpi, " throw");
+      if (d_right (mod))
+	{
+	  d_append_char (dpi, '(');
+	  d_print_comp (dpi, options, d_right (mod));
+	  d_append_char (dpi, ')');
+	}
+      return;
     case DEMANGLE_COMPONENT_VENDOR_TYPE_QUAL:
       d_append_char (dpi, ' ');
       d_print_comp (dpi, options, d_right (mod));
@@ -5864,12 +5915,7 @@ d_print_function_type (struct d_print_info *dpi, int options,
 	  need_space = 1;
 	  need_paren = 1;
 	  break;
-	case DEMANGLE_COMPONENT_RESTRICT_THIS:
-	case DEMANGLE_COMPONENT_VOLATILE_THIS:
-	case DEMANGLE_COMPONENT_CONST_THIS:
-	case DEMANGLE_COMPONENT_REFERENCE_THIS:
-	case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
-	case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
+	FNQUAL_COMPONENT_CASE:
 	  break;
 	default:
 	  break;
@@ -6411,7 +6457,6 @@ is_ctor_or_dtor (const char *mangled,
 	  case DEMANGLE_COMPONENT_CONST_THIS:
 	  case DEMANGLE_COMPONENT_REFERENCE_THIS:
 	  case DEMANGLE_COMPONENT_RVALUE_REFERENCE_THIS:
-	  case DEMANGLE_COMPONENT_TRANSACTION_SAFE:
 	  default:
 	    dc = NULL;
 	    break;
diff --git a/libiberty/testsuite/demangle-expected b/libiberty/testsuite/demangle-expected
index a567763..5badc3e 100644
--- a/libiberty/testsuite/demangle-expected
+++ b/libiberty/testsuite/demangle-expected
@@ -4596,3 +4596,13 @@ __10%0__S4_0T0T0
 # Inheriting constructor
 _ZN1DCI11BEi
 D::B(int)
+
+# exception-specification (C++17)
+_Z1fIvJiELb0EEvPDOT1_EFT_DpT0_E
+void f<void, int, false>(void (*)(int) noexcept(false))
+
+_Z1fIvJiELb0EEvPDoFT_DpT0_E
+void f<void, int, false>(void (*)(int) noexcept)
+
+_Z1fIvJiELb0EEvPDwiEFT_DpT0_E
+void f<void, int, false>(void (*)(int) throw(int))
diff --git a/libstdc++-v3/include/bits/c++config b/libstdc++-v3/include/bits/c++config
index 6d3226f..8a27d14 100644
--- a/libstdc++-v3/include/bits/c++config
+++ b/libstdc++-v3/include/bits/c++config
@@ -146,6 +146,14 @@
 # endif
 #endif
 
+#if __cpp_noexcept_function_type
+#define _GLIBCXX_NOEXCEPT_PARM , bool _N
+#define _GLIBCXX_NOEXCEPT_QUAL noexcept (_N)
+#else
+#define _GLIBCXX_NOEXCEPT_PARM
+#define _GLIBCXX_NOEXCEPT_QUAL
+#endif
+
 // Macro for extern template, ie controlling template linkage via use
 // of extern keyword on template declaration. As documented in the g++
 // manual, it inhibits all implicit instantiations and is used
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index 6824c9e..e5f2bba 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -458,100 +458,100 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct is_function
     : public false_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...)>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......)>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) const>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) const _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) const &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) const & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) const &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) const && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) const>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) const _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) const &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) const & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) const &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) const && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) volatile>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) volatile _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) volatile &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) volatile & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) volatile &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) volatile && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) volatile>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) volatile _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) volatile &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) volatile & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) volatile &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) volatile && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) const volatile>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) const volatile _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) const volatile &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) const volatile & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes...) const volatile &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes...) const volatile && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) const volatile>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) const volatile _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) const volatile &>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) const volatile & _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
-  template<typename _Res, typename... _ArgTypes>
-    struct is_function<_Res(_ArgTypes......) const volatile &&>
+  template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM>
+    struct is_function<_Res(_ArgTypes......) const volatile && _GLIBCXX_NOEXCEPT_QUAL>
     : public true_type { };
 
 #define __cpp_lib_is_null_pointer 201309
diff --git a/libstdc++-v3/libsupc++/cxxabi.h b/libstdc++-v3/libsupc++/cxxabi.h
index f4b8f75..7f1bd99 100644
--- a/libstdc++-v3/libsupc++/cxxabi.h
+++ b/libstdc++-v3/libsupc++/cxxabi.h
@@ -279,7 +279,8 @@ namespace __cxxabiv1
 	__restrict_mask = 0x4,
 	__incomplete_mask = 0x8,
 	__incomplete_class_mask = 0x10,
-	__transaction_safe_mask = 0x20
+	__transaction_safe_mask = 0x20,
+	__noexcept_mask = 0x40
       };
 
   protected:
diff --git a/libstdc++-v3/libsupc++/new b/libstdc++-v3/libsupc++/new
index 6bc6c00..1e59649 100644
--- a/libstdc++-v3/libsupc++/new
+++ b/libstdc++-v3/libsupc++/new
@@ -197,10 +197,10 @@ namespace std
   // The program is ill-formed if T is a function type or
   // (possibly cv-qualified) void.
 
-  template<typename _Ret, typename... _Args>
-    void launder(_Ret (*)(_Args...)) = delete;
-  template<typename _Ret, typename... _Args>
-    void launder(_Ret (*)(_Args......)) = delete;
+  template<typename _Ret, typename... _Args _GLIBCXX_NOEXCEPT_PARM>
+    void launder(_Ret (*)(_Args...) _GLIBCXX_NOEXCEPT_QUAL) = delete;
+  template<typename _Ret, typename... _Args _GLIBCXX_NOEXCEPT_PARM>
+    void launder(_Ret (*)(_Args......) _GLIBCXX_NOEXCEPT_QUAL) = delete;
 
   void launder(void*) = delete;
   void launder(const void*) = delete;
diff --git a/libstdc++-v3/libsupc++/pbase_type_info.cc b/libstdc++-v3/libsupc++/pbase_type_info.cc
index ff6b756..b2b9c09 100644
--- a/libstdc++-v3/libsupc++/pbase_type_info.cc
+++ b/libstdc++-v3/libsupc++/pbase_type_info.cc
@@ -80,12 +80,13 @@ __do_catch (const type_info *thr_type,
 
   unsigned tflags = thrown_type->__flags;
 
-  bool throw_tx = (tflags & __transaction_safe_mask);
-  bool catch_tx = (__flags & __transaction_safe_mask);
-  if (throw_tx && !catch_tx)
-    /* Catch can perform a transaction-safety conversion.  */
-    tflags &= ~__transaction_safe_mask;
-  if (catch_tx && !throw_tx)
+  const unsigned fqual_mask = __transaction_safe_mask|__noexcept_mask;
+  unsigned throw_fqual = (tflags & fqual_mask);
+  unsigned catch_fqual = (__flags & fqual_mask);
+  if (throw_fqual & ~catch_fqual)
+    /* Catch can perform a function pointer conversion.  */
+    tflags &= catch_fqual;
+  if (catch_fqual & ~throw_fqual)
     /* But not the reverse.  */
     return false;
   
diff --git a/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc b/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc
index 20bc198..15cebed 100644
--- a/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc
+++ b/libstdc++-v3/testsuite/ext/profile/mutex_extensions_neg.cc
@@ -25,7 +25,7 @@
 
 #include <vector>
 
-// { dg-error "multiple inlined namespaces" "" { target *-*-* } 342 }
+// { dg-error "multiple inlined namespaces" "" { target *-*-* } 350 }
 
 // "template argument 1 is invalid"
 // { dg-prune-output "tuple:993" }

commit 308929d4677dd3d932335bdede50453b27d9bd07
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Nov 3 15:51:08 2016 -0400

    C++ cleanups before C++17 noexcept in types.
    
            * call.c (standard_conversion): Reorganize pointer conversions.
            * pt.c (convert_nontype_argument_function): Convert to ref here.
            (convert_nontype_argument): Not here.
            (convert_template_argument): Add original type to error message.
            (RECUR_AND_CHECK_FAILURE): Remove trailing semicolon.
            (unify): Compare function-qualifiers.
            * typeck.c (same_type_ignoring_top_level_qualifiers_p): Use
            cp_build_qualified_type rather than TYPE_MAIN_VARIANT.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index d2e99bc..0466cd1 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -1212,19 +1212,40 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
       tree to_pointee;
       tree from_pointee;
 
+      if (tcode == POINTER_TYPE)
+	{
+	  to_pointee = TREE_TYPE (to);
+	  from_pointee = TREE_TYPE (from);
+
+	  /* Since this is the target of a pointer, it can't have function
+	     qualifiers, so any TYPE_QUALS must be for attributes const or
+	     noreturn.  Strip them.  */
+	  if (TREE_CODE (to_pointee) == FUNCTION_TYPE
+	      && TYPE_QUALS (to_pointee))
+	    to_pointee = build_qualified_type (to_pointee, TYPE_UNQUALIFIED);
+	  if (TREE_CODE (from_pointee) == FUNCTION_TYPE
+	      && TYPE_QUALS (from_pointee))
+	    from_pointee = build_qualified_type (from_pointee, TYPE_UNQUALIFIED);
+	}
+      else
+	{
+	  to_pointee = TYPE_PTRMEM_POINTED_TO_TYPE (to);
+	  from_pointee = TYPE_PTRMEM_POINTED_TO_TYPE (from);
+	}
+
       if (tcode == POINTER_TYPE
-	  && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (from),
-							TREE_TYPE (to)))
+	  && same_type_ignoring_top_level_qualifiers_p (from_pointee,
+							to_pointee))
 	;
-      else if (VOID_TYPE_P (TREE_TYPE (to))
+      else if (VOID_TYPE_P (to_pointee)
 	       && !TYPE_PTRDATAMEM_P (from)
-	       && TREE_CODE (TREE_TYPE (from)) != FUNCTION_TYPE)
+	       && TREE_CODE (from_pointee) != FUNCTION_TYPE)
 	{
 	  tree nfrom = TREE_TYPE (from);
 	  /* Don't try to apply restrict to void.  */
 	  int quals = cp_type_quals (nfrom) & ~TYPE_QUAL_RESTRICT;
-	  from = build_pointer_type
-	    (cp_build_qualified_type (void_type_node, quals));
+	  from_pointee = cp_build_qualified_type (void_type_node, quals);
+	  from = build_pointer_type (from_pointee);
 	  conv = build_conv (ck_ptr, from, conv);
 	}
       else if (TYPE_PTRDATAMEM_P (from))
@@ -1234,18 +1255,16 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
 
 	  if (DERIVED_FROM_P (fbase, tbase)
 	      && (same_type_ignoring_top_level_qualifiers_p
-		  (TYPE_PTRMEM_POINTED_TO_TYPE (from),
-		   TYPE_PTRMEM_POINTED_TO_TYPE (to))))
+		  (from_pointee, to_pointee)))
 	    {
-	      from = build_ptrmem_type (tbase,
-					TYPE_PTRMEM_POINTED_TO_TYPE (from));
+	      from = build_ptrmem_type (tbase, from_pointee);
 	      conv = build_conv (ck_pmem, from, conv);
 	    }
 	  else if (!same_type_p (fbase, tbase))
 	    return NULL;
 	}
-      else if (CLASS_TYPE_P (TREE_TYPE (from))
-	       && CLASS_TYPE_P (TREE_TYPE (to))
+      else if (CLASS_TYPE_P (from_pointee)
+	       && CLASS_TYPE_P (to_pointee)
 	       /* [conv.ptr]
 
 		  An rvalue of type "pointer to cv D," where D is a
@@ -1257,38 +1276,28 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
 		  that necessitates this conversion is ill-formed.
 		  Therefore, we use DERIVED_FROM_P, and do not check
 		  access or uniqueness.  */
-	       && DERIVED_FROM_P (TREE_TYPE (to), TREE_TYPE (from)))
+	       && DERIVED_FROM_P (to_pointee, from_pointee))
 	{
-	  from =
-	    cp_build_qualified_type (TREE_TYPE (to),
-				     cp_type_quals (TREE_TYPE (from)));
-	  from = build_pointer_type (from);
+	  from_pointee
+	    = cp_build_qualified_type (to_pointee,
+				       cp_type_quals (from_pointee));
+	  from = build_pointer_type (from_pointee);
 	  conv = build_conv (ck_ptr, from, conv);
 	  conv->base_p = true;
 	}
-      else if (tx_safe_fn_type_p (TREE_TYPE (from)))
+      else if (tx_safe_fn_type_p (from_pointee))
 	{
 	  /* A prvalue of type "pointer to transaction_safe function" can be
 	     converted to a prvalue of type "pointer to function". */
-	  tree unsafe = tx_unsafe_fn_variant (TREE_TYPE (from));
-	  if (same_type_p (unsafe, TREE_TYPE (to)))
+	  tree unsafe = tx_unsafe_fn_variant (from_pointee);
+	  if (same_type_p (unsafe, to_pointee))
 	    {
+	      from_pointee = unsafe;
 	      from = build_pointer_type (unsafe);
 	      conv = build_conv (ck_tsafe, from, conv);
 	    }
 	}
 
-      if (tcode == POINTER_TYPE)
-	{
-	  to_pointee = TREE_TYPE (to);
-	  from_pointee = TREE_TYPE (from);
-	}
-      else
-	{
-	  to_pointee = TYPE_PTRMEM_POINTED_TO_TYPE (to);
-	  from_pointee = TYPE_PTRMEM_POINTED_TO_TYPE (from);
-	}
-
       if (same_type_p (from, to))
 	/* OK */;
       else if (c_cast_p && comp_ptr_ttypes_const (to, from))
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index f910d40..45965aa 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -5913,11 +5913,11 @@ convert_nontype_argument_function (tree type, tree expr,
 	  error ("%qE is not a valid template argument for type %qT",
 		 expr, type);
 	  if (TYPE_PTR_P (type))
-	    error ("it must be the address of a function with "
-		   "external linkage");
+	    inform (input_location, "it must be the address of a function "
+		    "with external linkage");
 	  else
-	    error ("it must be the name of a function with "
-		   "external linkage");
+	    inform (input_location, "it must be the name of a function with "
+		    "external linkage");
 	}
       return NULL_TREE;
     }
@@ -5939,6 +5939,11 @@ convert_nontype_argument_function (tree type, tree expr,
       return NULL_TREE;
     }
 
+  if (TREE_CODE (type) == REFERENCE_TYPE)
+    fn = build_address (fn);
+  if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (fn)))
+    fn = build_nop (type, fn);
+
   return fn;
 }
 
@@ -6648,8 +6653,6 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
 	expr = convert_nontype_argument_function (type, expr, complain);
       if (!expr || expr == error_mark_node)
 	return expr;
-
-      expr = build_nop (type, build_address (expr));
     }
   /* [temp.arg.nontype]/5, bullet 6
 
@@ -7325,7 +7328,8 @@ convert_template_argument (tree parm,
       if (val == NULL_TREE)
 	val = error_mark_node;
       else if (val == error_mark_node && (complain & tf_error))
-	error ("could not convert template argument %qE to %qT",  orig_arg, t);
+	error ("could not convert template argument %qE from %qT to %qT",
+	       orig_arg, TREE_TYPE (orig_arg), t);
 
       if (INDIRECT_REF_P (val))
         {
@@ -19354,7 +19358,7 @@ template_parm_level_and_index (tree parm, int* level, int* index)
   do {									\
     if (unify (TP, TA, P, A, S, EP))					\
       return 1;								\
-  } while (0);
+  } while (0)
 
 /* Unifies the remaining arguments in PACKED_ARGS with the pack
    expansion at the end of PACKED_PARMS. Returns 0 if the type
@@ -20312,6 +20316,11 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
 		 class_of_this_parm (arg),
 		 class_of_this_parm (parm))))
 	  return unify_cv_qual_mismatch (explain_p, parm, arg);
+	if (TREE_CODE (arg) == FUNCTION_TYPE
+	    && type_memfn_quals (parm) != type_memfn_quals (arg))
+	  return unify_cv_qual_mismatch (explain_p, parm, arg);
+	if (type_memfn_rqual (parm) != type_memfn_rqual (arg))
+	  return unify_type_mismatch (explain_p, parm, arg);
 
 	RECUR_AND_CHECK_FAILURE (tparms, targs, TREE_TYPE (parm),
 				 TREE_TYPE (arg), UNIFY_ALLOW_NONE, explain_p);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 45b7d26..4ff2bc2 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1457,7 +1457,9 @@ same_type_ignoring_top_level_qualifiers_p (tree type1, tree type2)
   if (type1 == error_mark_node || type2 == error_mark_node)
     return false;
 
-  return same_type_p (TYPE_MAIN_VARIANT (type1), TYPE_MAIN_VARIANT (type2));
+  type1 = cp_build_qualified_type (type1, TYPE_UNQUALIFIED);
+  type2 = cp_build_qualified_type (type2, TYPE_UNQUALIFIED);
+  return same_type_p (type1, type2);
 }
 
 /* Returns 1 if TYPE1 is at least as qualified as TYPE2.  */
diff --git a/gcc/testsuite/g++.dg/template/func2.C b/gcc/testsuite/g++.dg/template/func2.C
index b0f691d..746f0e2 100644
--- a/gcc/testsuite/g++.dg/template/func2.C
+++ b/gcc/testsuite/g++.dg/template/func2.C
@@ -5,7 +5,7 @@ fptr zeroptr = 0;
 template<typename T, fptr F> struct foo { };
 template<typename T> struct foo<T,zeroptr> { };
 // { dg-error "not a valid template argument" "not valid" { target *-*-* } 6 } 
-// { dg-error "must be the address" "must be the address " { target *-*-* } 6 }
+// { dg-message "must be the address" "must be the address " { target *-*-* } 6 }
 
 // The rest is needed to trigger the ICE in 4.0 to 4.3:
 void f() { }


More information about the Libstdc++ mailing list