Patchwork [C++] __builtin_choose_expr

login
register
mail settings
Submitter Andy Gibbs
Date Oct. 18, 2011, 6:22 a.m.
Message ID <BLU0-SMTP105B5D54447362C06B25172E7E50@phx.gbl>
Download mbox | patch
Permalink /patch/120366/
State New
Headers show

Comments

Andy Gibbs - Oct. 18, 2011, 6:22 a.m.
Hi,

This is a more developed version of a patch originally posted to the gcc-help
mailing list (http://gcc.gnu.org/ml/gcc-help/2011-10/msg00062.html).  The
purpose of the patch is to implement the __builtin_choose_expr feature which
has already been available to C code but not previously to C++ code.

Paraphased from the offical docs:

__builtin_choose_expr evaluates one of two expressions based on a compile-time
evaluated condition.  This built-in function is analogous to the `? :' operator
except that the expression returned has its type unaltered by promotion rules.
[see http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html]

This patch applies this to C++, but as an extension, if the condition can be
evaluated during the parse (e.g. it does not rely on a template parameter) then
the non-evaluated expression will not be parsed at all, rather than simply not
evaluated.  This enables __builtin_choose_expr to be used where the unchosen
expression would otherwise cause a compiler warning/error.

The caveat of this is that the parser needs to be able to step over an entire
expression.  The solution that I came up with for this is to use the existing
cp_parser_skip_to_closing_parenthesis function and scan for either closing
parenthesis or comma.  Unfortunately, this isn't fool-proof since if the
expression uses comma which is not in some way encapsulated in parenthesis,
braces or square brackets then the scan will conclude early causing a compile
error.  An example of a problem expression, therefore, is "A<T,S>::func()" since
the lexer cannot deduce between "A<T" as a comparison expression and the whole
as a template expression.  Simply enclosing the entire expression in parenthesis
(i.e. "(A<T,S>::func())") solves the problem.

The patch causes no regressions as far as I know.  Here is an excerpt from the
testsuite log *prior* to the patch:

                === gcc Summary ===

# of expected passes            81909
# of unexpected failures        33
# of unexpected successes       20
# of expected failures          271
# of unsupported tests          1995
/home/user/gcc-build/gcc/xgcc  version 4.7.0 20111017 (experimental) (GCC) 

                === g++ tests ===


Running target unix
FAIL: g++.dg/guality/redeclaration1.C  -O0  line 17 i == 24
FAIL: g++.dg/guality/redeclaration1.C  -O1  line 17 i == 24
FAIL: g++.dg/guality/redeclaration1.C  -O2  line 17 i == 24
FAIL: g++.dg/guality/redeclaration1.C  -O3 -fomit-frame-pointer  line 17 i == 24
FAIL: g++.dg/guality/redeclaration1.C  -O3 -g  line 17 i == 24
FAIL: g++.dg/guality/redeclaration1.C  -Os  line 17 i == 24
FAIL: g++.dg/lto/20100302 cp_lto_20100302_0.o-cp_lto_20100302_1.o link, -flto -fabi-version=2 (internal compiler error)
UNRESOLVED: g++.dg/lto/20100302 cp_lto_20100302_0.o-cp_lto_20100302_1.o execute -flto -fabi-version=2

                === g++ Summary ===

# of expected passes            28223
# of unexpected failures        7
# of expected failures          155
# of unresolved testcases       1
# of unsupported tests          160
/home/user/gcc-build/gcc/testsuite/g++/../../g++  version 4.7.0 20111017 (experimental) (GCC) 

                === libstdc++ tests ===


Running target unix

                === libstdc++ Summary ===

# of expected passes            8055
# of expected failures          46
# of unsupported tests          469

Compiler version: 4.7.0 20111017 (experimental) (GCC) 
Platform: i686-pc-linux-gnu
configure flags: --enable-lto --enable-threads=posix --enable-__cxa_atexit --enable-languages=c,c++ --enable-libssp --disable-libmudflap --enable-libgomp --disable-nls --enable-c99 --enable-long-long --enable-stage1-languages=c,c++ --enable-checking


And here afterwards (only the section showing a change):

                === g++ Summary ===

# of expected passes            28278 [+55]
# of unexpected failures        7
# of expected failures          155
# of unresolved testcases       1
# of unsupported tests          160


This is my first patch contributed to GCC.  I hope that I have met the required
quality standard!  I will be happy to answer any questions / reformat the patch
as necessary...

Regards,
Andy
Paolo Carlini - Oct. 18, 2011, 10 a.m.
I'm under the impression that some tests are written in C, wouldn't 
better fit in c-c++-common?

Thanks,
Paolo.
Andy Gibbs - Oct. 18, 2011, 11:36 a.m.
----- Original Message ----- 
From: "Paolo Carlini" <paolo.carlini@oracle.com>
To: "Andy Gibbs" <andyg1001@hotmail.co.uk>
Cc: <gcc-patches@gcc.gnu.org>; <iant@google.com>
Sent: Tuesday, October 18, 2011 12:00 PM
Subject: Re: [C++ Patch] __builtin_choose_expr


> I'm under the impression that some tests are written in C, wouldn't better 
> fit in c-c++-common?

Ok, I can do this but I think only 2 out of the 8 tests can be moved
across.  I will then move the duplicates out of the gcc.dg folder too.

I will post a replacement patch later this afternoon.

Andy

Patch

=====

Changelog:

2011-10-17  Andy Gibbs <andyg1001@hotmail.co.uk>

        * c-family/c-common.c: Enable __builtin_choose_expr

        * cp/cp-tree.def: Add tree node for handling __builtin_choose_expr
          within a template scenario
        * cp/cp-tree.h: Ditto
        * cp/cp-objcp-common.c: Ditto
        * cp/decl.c: Ditto

        * cp/parser.c (cp_parser_skip_to_closing_parenthesis): Remove the
          unnecessary requirement for 'recovering' flag to be set when
          'or_comma' flag is set
        * cp/parser.c (cp_parser_skip_to_end_of_parameter): New function
        * cp/parser.c (cp_parser_builtin_choose_expr): New function
        * cp/parser.c (cp_parser_primary_expression): Use it
        * cp/semantics.c (potential_constant_expression_1): Mark the use
          of __builtin_choose_expr as a potentially constant expression
        * cp/semantics.c (evaluate_builtin_choose_expr_condition): New
          function
        * cp/semantics.c (finish_builtin_choose_expr): New function
        * cp/pt.c (tsubst_copy_and_build): Use it

        * cp/cxx-pretty-print.h: New function pp_cxx_choose_expression
        * cp/cxx-pretty-print.c: Document, define and use it
        * cp/error.c: Use it

        * doc/extend.text: Update docs

        * testsuite/g++.dg/ext/builtin-choose-expr1.C: New test
        * testsuite/g++.dg/ext/builtin-choose-expr2.C: New test
        * testsuite/g++.dg/ext/builtin-choose-expr3.C: New test
        * testsuite/g++.dg/ext/builtin-choose-expr4.C: New test
        * testsuite/g++.dg/ext/builtin-choose-expr5.C: New test
        * testsuite/g++.dg/ext/builtin-choose-expr6.C: New test
        * testsuite/g++.dg/ext/builtin-choose-expr7.C: New test
        * testsuite/g++.dg/ext/builtin-choose-expr8.C: New test


--- gcc-4.7.0-r180072/gcc/c-family/c-common.c
+++ gcc-4.7.0-patched/gcc/c-family/c-common.c
@@ -423,7 +423,7 @@  const struct c_common_resword c_common_r
   { "__asm__",         RID_ASM,        0 },
   { "__attribute",     RID_ATTRIBUTE,  0 },
   { "__attribute__",   RID_ATTRIBUTE,  0 },
-  { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
+  { "__builtin_choose_expr", RID_CHOOSE_EXPR, 0 },
   { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
   { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, D_CONLY },
   { "__builtin_offsetof", RID_OFFSETOF, 0 },
--- gcc-4.7.0-r180072/gcc/cp/cp-objcp-common.c
+++ gcc-4.7.0-patched/gcc/cp/cp-objcp-common.c
@@ -100,6 +100,8 @@  cp_tree_size (enum tree_code code)
 
     case TEMPLATE_INFO:         return sizeof (struct tree_template_info);
 
+    case CHOOSE_EXPR:           return sizeof (struct tree_choose_expr);
+
     default:
       gcc_unreachable ();
     }
@@ -301,6 +303,7 @@  cp_common_init_ts (void)
   MARK_TS_TYPED (USING_STMT);
   MARK_TS_TYPED (LAMBDA_EXPR);
   MARK_TS_TYPED (CTOR_INITIALIZER);
+  MARK_TS_TYPED (CHOOSE_EXPR);
 }
 
 #include "gt-cp-cp-objcp-common.h"
--- gcc-4.7.0-r180072/gcc/cp/cp-tree.def
+++ gcc-4.7.0-patched/gcc/cp/cp-tree.def
@@ -332,6 +332,9 @@  DEFTREECODE (TAG_DEFN, "tag_defn", tcc_e
 /* Represents an 'offsetof' expression during template expansion.  */
 DEFTREECODE (OFFSETOF_EXPR, "offsetof_expr", tcc_expression, 1)
 
+/* Represents an '__builtin_choose_expr' expression during template expansion.  */
+DEFTREECODE (CHOOSE_EXPR, "choose_expr", tcc_exceptional, 0)
+
 /* Represents a 'sizeof' expression during template expansion.  */
 DEFTREECODE (SIZEOF_EXPR, "sizeof_expr", tcc_expression, 1)
 
--- gcc-4.7.0-r180072/gcc/cp/cp-tree.h
+++ gcc-4.7.0-patched/gcc/cp/cp-tree.h
@@ -532,6 +532,33 @@  struct GTY (()) tree_deferred_noexcept {
 };
 
 
+/* The condition associated with __builtin_choose_expr.  This must be
+   an integral constant expression.  */
+#define CHOOSE_EXPR_CONDITION(NODE) \
+  (((struct tree_choose_expr *)CHOOSE_EXPR_CHECK (NODE))->condition)
+
+/* The expression to be used when condition in __builtin_choose_expr
+   is true*/
+#define CHOOSE_EXPR_TRUE(NODE) \
+  (((struct tree_choose_expr *)CHOOSE_EXPR_CHECK (NODE))->expr_true)
+
+/* The expression to be used when condition in __builtin_choose_expr
+   is false*/
+#define CHOOSE_EXPR_FALSE(NODE) \
+  (((struct tree_choose_expr *)CHOOSE_EXPR_CHECK (NODE))->expr_false)
+
+/* Source location information for __builtin_choose_expr.  */
+#define CHOOSE_EXPR_SOURCE_LOCATION(NODE) \
+  (((struct tree_choose_expr *)CHOOSE_EXPR_CHECK (NODE))->location)
+
+struct GTY (()) tree_choose_expr {
+  struct tree_common common;
+  tree condition;
+  tree expr_true;
+  tree expr_false;
+  location_t location;
+};
+
 /* The condition associated with the static assertion.  This must be
    an integral constant expression.  */
 #define STATIC_ASSERT_CONDITION(NODE) \
@@ -738,6 +765,7 @@  enum cp_tree_node_structure_enum {
   TS_CP_TRAIT_EXPR,
   TS_CP_LAMBDA_EXPR,
   TS_CP_TEMPLATE_INFO,
+  TS_CP_CHOOSE_EXPR,
   LAST_TS_CP_ENUM
 };
 
@@ -763,6 +791,7 @@  union GTY((desc ("cp_tree_node_structure
     lambda_expression;
   struct tree_template_info GTY ((tag ("TS_CP_TEMPLATE_INFO")))
     template_info;
+  struct tree_choose_expr GTY ((tag ("TS_CP_CHOOSE_EXPR"))) choose_expr;
 };
 
 
@@ -5463,6 +5492,8 @@  extern tree finish_id_expression          (tree,
 extern tree finish_typeof                      (tree);
 extern tree finish_underlying_type             (tree);
 extern tree finish_offsetof                    (tree);
+extern bool evaluate_builtin_choose_expr_condition (tree, location_t, bool *);
+extern tree finish_builtin_choose_expr (tree, tree, tree, location_t);
 extern void finish_decl_cleanup                        (tree, tree);
 extern void finish_eh_cleanup                  (tree);
 extern void emit_associated_thunks             (tree);
--- gcc-4.7.0-r180072/gcc/cp/cxx-pretty-print.c
+++ gcc-4.7.0-patched/gcc/cp/cxx-pretty-print.c
@@ -379,6 +379,7 @@  pp_cxx_id_expression (cxx_pretty_printer
    GNU Extensions:
      __builtin_va_arg ( assignment-expression , type-id )
      __builtin_offsetof ( type-id, offsetof-expression )
+     __builtin_choose_expr ( constant-expression , assignment-expression , assignment-expression )
 
      __has_nothrow_assign ( type-id )   
      __has_nothrow_constructor ( type-id )
@@ -450,6 +451,10 @@  pp_cxx_primary_expression (cxx_pretty_pr
       pp_cxx_offsetof_expression (pp, t);
       break;
 
+    case CHOOSE_EXPR:
+      pp_cxx_choose_expression (pp, t);
+      break;
+
     default:
       pp_c_primary_expression (pp_c_base (pp), t);
       break;
@@ -2312,6 +2317,19 @@  pp_cxx_offsetof_expression (cxx_pretty_p
   pp_cxx_right_paren (pp);
 }
 
+void
+pp_cxx_choose_expression (cxx_pretty_printer *pp, tree t)
+{
+  pp_cxx_ws_string (pp, "__builtin_choose_expr");
+  pp_cxx_left_paren (pp);
+  pp_cxx_expression (pp, CHOOSE_EXPR_CONDITION (t));
+  pp_cxx_separate_with (pp, ',');
+  pp_cxx_expression (pp, CHOOSE_EXPR_TRUE (t));
+  pp_cxx_separate_with (pp, ',');
+  pp_cxx_expression (pp, CHOOSE_EXPR_FALSE (t));
+  pp_cxx_right_paren (pp);
+}
+
 void
 pp_cxx_trait_expression (cxx_pretty_printer *pp, tree t)
 {
--- gcc-4.7.0-r180072/gcc/cp/cxx-pretty-print.h
+++ gcc-4.7.0-patched/gcc/cp/cxx-pretty-print.h
@@ -74,5 +74,6 @@  void pp_cxx_canonical_template_parameter
 void pp_cxx_trait_expression (cxx_pretty_printer *, tree);
 void pp_cxx_va_arg_expression (cxx_pretty_printer *, tree);
 void pp_cxx_offsetof_expression (cxx_pretty_printer *, tree);
+void pp_cxx_choose_expression (cxx_pretty_printer *, tree);
 
 #endif /* GCC_CXX_PRETTY_PRINT_H */
--- gcc-4.7.0-r180072/gcc/cp/decl.c
+++ gcc-4.7.0-patched/gcc/cp/decl.c
@@ -13749,6 +13749,7 @@  cp_tree_node_structure (union lang_tree_
     case TRAIT_EXPR:           return TS_CP_TRAIT_EXPR;
     case LAMBDA_EXPR:          return TS_CP_LAMBDA_EXPR;
     case TEMPLATE_INFO:                return TS_CP_TEMPLATE_INFO;
+    case CHOOSE_EXPR:          return TS_CP_CHOOSE_EXPR;
     default:                   return TS_CP_GENERIC;
     }
 }
--- gcc-4.7.0-r180072/gcc/cp/error.c
+++ gcc-4.7.0-patched/gcc/cp/error.c
@@ -2368,6 +2368,10 @@  dump_expr (tree t, int flags)
       pp_cxx_offsetof_expression (cxx_pp, t);
       break;
 
+    case CHOOSE_EXPR:
+      pp_cxx_choose_expression (cxx_pp, t);
+      break;
+
     case SCOPE_REF:
       dump_decl (t, flags);
       break;
--- gcc-4.7.0-r180072/gcc/cp/parser.c
+++ gcc-4.7.0-patched/gcc/cp/parser.c
@@ -1595,6 +1595,8 @@  static tree cp_parser_expression
   (cp_parser *, bool, cp_id_kind *);
 static tree cp_parser_constant_expression
   (cp_parser *, bool, bool *);
+static tree cp_parser_builtin_choose_expr
+  (cp_parser *);
 static tree cp_parser_builtin_offsetof
   (cp_parser *);
 static tree cp_parser_lambda_expression
@@ -2023,6 +2025,8 @@  static bool cp_parser_parse_and_diagnose
   (cp_parser *);
 static int cp_parser_skip_to_closing_parenthesis
   (cp_parser *, bool, bool, bool);
+static bool cp_parser_skip_to_end_of_parameter
+  (cp_parser *);
 static void cp_parser_skip_to_end_of_statement
   (cp_parser *);
 static void cp_parser_consume_semicolon_at_end_of_statement
@@ -2678,7 +2682,7 @@  cp_parser_skip_to_closing_parenthesis (c
          break;
 
        case CPP_COMMA:
-         if (recovering && or_comma && !brace_depth && !paren_depth
+         if (or_comma && !brace_depth && !paren_depth
              && !square_depth)
            return -1;
          break;
@@ -2706,6 +2710,20 @@  cp_parser_skip_to_closing_parenthesis (c
     }
 }
 
+/* Consume tokens until we reach the end of the current parameter,
+   which will be just before consuming a `)' or a `,'.  Will abort
+   if a non-nested `}', `]' or `;' is reached or we run out of
+   tokens.  Returns true on success, false otherwise.               */
+
+static bool
+cp_parser_skip_to_end_of_parameter (cp_parser* parser)
+{
+  /* FIXME Uses cp_parser_skip_to_closing_parenthesis, but this fails
+     to correctly interpret nested commas inside template `<' and `>' */
+  return (cp_parser_skip_to_closing_parenthesis (parser,
+                                          false, true, false) != 0);
+}
+
 /* Consume tokens until we reach the end of the current statement.
    Normally, that will be just before consuming a `;'.  However, if a
    non-nested `}' comes first, then we stop before consuming that.  */
@@ -3287,6 +3305,7 @@  cp_parser_translation_unit (cp_parser* p
      ( compound-statement )
      __builtin_va_arg ( assignment-expression , type-id )
      __builtin_offsetof ( type-id , offsetof-expression )
+     __builtin_choose_expr ( constant-expression , assignment-expression , assignment-expression )
 
    C++ Extensions:
      __has_nothrow_assign ( type-id )   
@@ -3619,6 +3638,9 @@  cp_parser_primary_expression (cp_parser
        case RID_OFFSETOF:
          return cp_parser_builtin_offsetof (parser);
 
+       case RID_CHOOSE_EXPR:
+         return cp_parser_builtin_choose_expr (parser);
+
        case RID_HAS_NOTHROW_ASSIGN:
        case RID_HAS_NOTHROW_CONSTRUCTOR:
        case RID_HAS_NOTHROW_COPY:        
@@ -7223,6 +7245,84 @@  cp_parser_builtin_offsetof (cp_parser *p
 
   return expr;
 }
+
+/* Parse __builtin_choose_expr.
+
+     __builtin_choose_expr ( constant-expression ,
+                 assignment-expression ,
+                 assignment-expression )  */
+
+static tree
+cp_parser_builtin_choose_expr (cp_parser *parser)
+{
+  tree expr, condition;
+
+  /* Peek at the "__builtin_choose_expr" token so we can 
+     keep track of exactly where it started.  */
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  location_t saved_loc = token->location;
+
+  /* Consume the "__builtin_choose_expr" token.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  /* Parse expressions. */
+  cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
+  condition = cp_parser_constant_expression (parser, false, NULL);
+  cp_parser_require (parser, CPP_COMMA, RT_COMMA);
+
+  if (processing_template_decl &&
+        (type_dependent_expression_p (condition) ||
+         value_dependent_expression_p (condition)))
+    {
+    /* We're in a template and can't evaluate direcly; build a
+       CHOOSE_EXPR node instead.  In doing so, we lose some
+       flexibility since both expressions are parsed and may
+       generate errors.                                        */
+    tree expr_true, expr_false;
+
+    expr_true = cp_parser_assignment_expression (parser, false, NULL);
+    cp_parser_require (parser, CPP_COMMA, RT_COMMA);
+    expr_false = cp_parser_assignment_expression (parser, false, NULL);
+
+    expr = make_node (CHOOSE_EXPR);
+    CHOOSE_EXPR_CONDITION (expr) = condition;
+    CHOOSE_EXPR_TRUE (expr) = expr_true;
+    CHOOSE_EXPR_FALSE (expr) = expr_false;
+    CHOOSE_EXPR_SOURCE_LOCATION (expr) = saved_loc;
+    }
+  else
+    {
+    /* We can evaluate directly which enables us the flexibility of
+       only parsing the expression to be evaluated.                 */
+    bool error = false;
+    bool cond_eval = evaluate_builtin_choose_expr_condition (condition,
+                                                     saved_loc, &error);
+    if (error)
+      {
+      cp_parser_skip_to_closing_parenthesis (parser, true, false, true);
+      return error_mark_node;
+      }
+
+    if (cond_eval)
+      {
+      expr = cp_parser_assignment_expression (parser, false, NULL);
+      cp_parser_require (parser, CPP_COMMA, RT_COMMA);
+      cp_parser_skip_to_end_of_parameter (parser);
+      }
+    else
+      {
+      cp_parser_skip_to_end_of_parameter (parser);
+      cp_parser_require (parser, CPP_COMMA, RT_COMMA);
+      expr = cp_parser_assignment_expression (parser, false, NULL);
+      }
+    }
+
+  /* Reach end of expression */
+  if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+    cp_parser_skip_to_closing_parenthesis (parser, true, false, true);
+
+  return expr;
+}
 
 /* Parse a trait expression.
 
--- gcc-4.7.0-r180072/gcc/cp/pt.c
+++ gcc-4.7.0-patched/gcc/cp/pt.c
@@ -13901,6 +13901,20 @@  tsubst_copy_and_build (tree t,
     case OFFSETOF_EXPR:
       return finish_offsetof (RECUR (TREE_OPERAND (t, 0)));
 
+    case CHOOSE_EXPR:
+      {
+      tree condition =
+          tsubst_expr (CHOOSE_EXPR_CONDITION (t), args, complain, in_decl,
+                       /*integral_constant_expression_p=*/true);
+      tree expr_true =
+          tsubst_expr (CHOOSE_EXPR_TRUE (t), args, complain, in_decl, false);
+      tree expr_false =
+          tsubst_expr (CHOOSE_EXPR_FALSE (t), args, complain, in_decl, false);
+
+      return finish_builtin_choose_expr (condition, expr_true, expr_false,
+                              CHOOSE_EXPR_SOURCE_LOCATION (t));
+      }
+
     case TRAIT_EXPR:
       {
        tree type1 = tsubst_copy (TRAIT_EXPR_TYPE1 (t), args,
--- gcc-4.7.0-r180072/gcc/cp/semantics.c
+++ gcc-4.7.0-patched/gcc/cp/semantics.c
@@ -3427,6 +3427,43 @@  finish_offsetof (tree expr)
   return fold_offsetof (expr, NULL_TREE);
 }
 
+/* Evaluate the condition clause of __builtin_choose_expr. */
+
+bool
+evaluate_builtin_choose_expr_condition (tree condition, location_t location, bool *error_p)
+{
+  location_t saved_loc = input_location;
+
+  /* Fold the condition expression and convert it to a boolean value. */
+  condition = fold_non_dependent_expr (condition);
+  condition = cp_convert (boolean_type_node, condition);
+  condition = maybe_constant_value (condition);
+
+  if (TREE_CODE (condition) == INTEGER_CST)
+    {
+    if (error_p) *error_p = false;
+    return !integer_zerop (condition);
+    }
+
+  input_location = location;
+  error ("first argument to %<__builtin_choose_expr%> not a constant");
+  cxx_constant_value (condition);
+  input_location = saved_loc;
+  if (error_p) *error_p = true;
+  return false;
+}
+
+/* Finish __builtin_choose_expr. */
+
+tree
+finish_builtin_choose_expr (tree condition, tree expr_true, tree expr_false,
+                            location_t location)
+{
+  bool error = false;
+  bool result = evaluate_builtin_choose_expr_condition (condition, location, &error);
+  return error ? error_mark_node : (result ? expr_true : expr_false);
+}
+
 /* Replace the AGGR_INIT_EXPR at *TP with an equivalent CALL_EXPR.  This
    function is broken out from the above for the benefit of the tree-ssa
    project.  */
@@ -7717,6 +7754,7 @@  potential_constant_expression_1 (tree t,
     case SIZEOF_EXPR:
     case ALIGNOF_EXPR:
     case OFFSETOF_EXPR:
+    case CHOOSE_EXPR:
     case NOEXCEPT_EXPR:
     case TEMPLATE_PARM_INDEX:
     case TRAIT_EXPR:
--- gcc-4.7.0-r180072/gcc/doc/extend.texi
+++ gcc-4.7.0-patched/gcc/doc/extend.texi
@@ -7544,10 +7544,9 @@  Example:
       (void)0))
 @end smallexample
 
-@emph{Note:} This construct is only available for C@.  Furthermore, the
-unused expression (@var{exp1} or @var{exp2} depending on the value of
-@var{const_exp}) may still generate syntax errors.  This may change in
-future revisions.
+@emph{Note:} The unused expression (@var{exp1} or @var{exp2} depending
+on the value of @var{const_exp}) may still generate syntax errors.
+This may change in future revisions.
 
 @end deftypefn
 
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr1.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr1.C
@@ -0,0 +1,28 @@ 
+/* Test for invalid use of __builtin_choose_expr.  */
+/* { dg-do compile } */
+/* { dg-options "-pedantic-errors" } */
+
+#include <limits.h>
+
+int a, b, c;
+
+void
+f (void)
+{
+  /* __builtin_choose_expr acts exactly like the chosen argument for
+     all constant expression purposes.  */
+  enum e {
+    E1 = __builtin_choose_expr (1, 1, ++b),
+    E2 = __builtin_choose_expr (0, 1, ++b) /* { dg-error "cannot appear" } */
+  };
+  /* The first argument to __builtin_choose_expr must be an integer
+     constant expression.  */
+  a = __builtin_choose_expr ((void *)0, b, c); /* { dg-error "cannot appear" } */
+  /* { dg-error "first argument" "" { target *-*-* } 20 } */
+  a = __builtin_choose_expr (0 * (INT_MAX + 1), b, c); /* { dg-warning "integer overflow in expression" } */
+  a = __builtin_choose_expr (1 / 0, 0, 0); /* { dg-warning "division by zero" } */
+  /* { dg-error "first argument" "" { target *-*-* } 23 } */
+  /* { dg-error "not a constant" "" { target *-*-* } 23 } */
+  a = __builtin_choose_expr ((1 ? 1 : a), b, c); /* { dg-error "cannot appear" } */
+  /* { dg-error "first argument" "" { target *-*-* } 26 } */
+}
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr2.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr2.C
@@ -0,0 +1,27 @@ 
+/* Test for invalid use of __builtin_choose_expr.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c++0x -pedantic-errors" } */
+
+#include <limits.h>
+
+int a, b, c;
+
+void
+f (void)
+{
+  /* __builtin_choose_expr acts exactly like the chosen argument for
+     all constant expression purposes.  */
+  enum e {
+    E1 = __builtin_choose_expr (1, 1, ++b),
+    E2 = __builtin_choose_expr (0, 1, ++b) /* { dg-error "not a constant-expression" } */
+    /* { dg-error "enumerator value" "" { target *-*-* } 16 } */
+  };
+  /* The first argument to __builtin_choose_expr must be an integer
+     constant expression.  */
+  a = __builtin_choose_expr ((void *)0, b, c);
+  a = __builtin_choose_expr (0 * (INT_MAX + 1), b, c); /* { dg-warning "overflow" } */
+  /* { dg-error "overflow" "" { target *-*-* } 22 } */
+  a = __builtin_choose_expr (1 / 0, 0, 0); /* { dg-warning "division by zero" } */
+  /* { dg-error "not a constant" "" { target *-*-* } 24 } */
+  a = __builtin_choose_expr ((1 ? 1 : a), b, c);
+}
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr3.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr3.C
@@ -0,0 +1,20 @@ 
+/* Test for valid use of __builtin_choose_expr.  */
+/* { dg-do compile } */
+/* { dg-options "-Wunused" } */
+
+int
+f1 (void)
+{
+  int a;
+  int b;
+  int c;
+  int d;
+  int e; /* { dg-warning "set but not used" } */
+  a = 1;
+  b = 2;
+  c = 3;
+  d = 4;
+  e = 5;
+  return sizeof (a) + ((__typeof (b)) 1) + __alignof__ (c)
+         + __builtin_choose_expr (1, d, e);
+}
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr4.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr4.C
@@ -0,0 +1,95 @@ 
+/* Test for valid use of __builtin_choose_expr.  */
+/* { dg-do run } */
+/* { dg-options "-O1 -Wall" } */
+
+#define choose __builtin_choose_expr
+
+/* Check the type of __builtin_choose_expr between E1 and E2, both
+   ways round and with both 0 and 1 as the condition.  */
+#define ASSERT_COND_TYPE(E1, E2)                               \
+        do {                                                   \
+          typedef __typeof(E1) T1;                             \
+          typedef __typeof(E2) T2;                             \
+          typedef T1 **T1pp;                                   \
+          typedef T2 **T2pp;                                   \
+          typedef __typeof(choose (1, (E1), (E2))) T1a;        \
+          typedef __typeof(choose (0, (E2), (E1))) T1b;        \
+          typedef __typeof(choose (1, (E2), (E1))) T2a;        \
+          typedef __typeof(choose (0, (E1), (E2))) T2b;        \
+          typedef T1a **T1app;                                 \
+          typedef T1b **T1bpp;                                 \
+          typedef T2a **T2app;                                 \
+          typedef T2b **T2bpp;                                 \
+          T1pp t1 = 0;                                         \
+          T2pp t2 = 0;                                         \
+          T1app t1a = 0;                                       \
+          T1bpp t1b = 0;                                       \
+          T2app t2a = 0;                                       \
+          T2bpp t2b = 0;                                       \
+          t1 = t1a;                                            \
+          t1 = t1b;                                            \
+          t2 = t2a;                                            \
+          t2 = t2b;                                            \
+          (void) t1;                                           \
+          (void) t2;                                           \
+        } while (0)
+
+
+extern "C" void abort ();
+extern "C" void exit (int);
+
+bool bad ()
+{
+  return false;
+}
+
+bool good ()
+{
+  return true;
+}
+
+int main (void)
+{
+  signed char sc1, sc2;
+  void *v1;
+  int i, j;
+  double dd;
+  float f;
+  typedef void (*fpt)(void);
+  fpt triple;
+  struct S { int x, y; } pour, some, sugar;
+  union u { int p; } united, nations;
+
+  if (__builtin_choose_expr (0, 12, 0)
+      || !__builtin_choose_expr (45, 5, 0)
+      || !__builtin_choose_expr (45, 3, 0))
+    abort ();
+
+  ASSERT_COND_TYPE (sc1, sc2);
+  ASSERT_COND_TYPE (v1, sc1);
+  ASSERT_COND_TYPE (i, j);
+  ASSERT_COND_TYPE (dd, main);
+  ASSERT_COND_TYPE ((float)dd, i);
+  ASSERT_COND_TYPE (4, f);
+  ASSERT_COND_TYPE (triple, some);
+  ASSERT_COND_TYPE (united, nations);
+  ASSERT_COND_TYPE (nations, main);
+
+  pour.y = 69;
+  __builtin_choose_expr (0, bad (), sugar) = pour;
+  if (sugar.y != 69)
+    abort ();
+
+  __builtin_choose_expr (sizeof (int), f, bad ()) = 3.5F;
+
+  if (f != 3.5F)
+    abort ();
+
+  if (!__builtin_choose_expr (1, good, bad)())
+    abort ();
+
+  if (!__builtin_choose_expr (0, bad, good)())
+    abort ();
+
+  exit (0);
+}
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr5.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr5.C
@@ -0,0 +1,14 @@ 
+/* Test for invalid use of __builtin_choose_expr.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+int a, b, c, d;
+
+void
+f (void)
+{
+  a = __builtin_choose_expr (b, c, d); /* { dg-error "'b' cannot appear in a constant-expression" } */
+  /* { dg-error "first argument to '__builtin_choose_expr' not a constant" "" { target *-*-* } 10 } */
+  a = __builtin_choose_expr (1, c); /* { dg-error "expected ','" } */
+  a = __builtin_choose_expr (1, b, c, d); /* { dg-error "expected '\\)'" } */
+}
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr6.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr6.C
@@ -0,0 +1,141 @@ 
+/* Test for valid use of __builtin_choose_expr.  */
+/* { dg-do compile } */
+/* { dg-options "-std=c++0x" } */
+
+template <typename, typename> struct is_same { enum : bool { value = false }; };
+template <typename T> struct is_same<T, T>   { enum : bool { value = true  }; };
+
+#define CHECK(expr, type, val) \
+  static_assert(is_same<decltype(expr), type>::value && (expr == val), "oops");
+
+#define CHECK_TYPE(expr, type) \
+  static_assert(is_same<decltype(expr), type>::value, "oops");
+
+int a, c, d;
+constexpr int b = 0;
+
+template <int i>
+struct s { static constexpr int value = i; };
+
+template <typename T>
+inline T declval_noref()
+  {
+  struct helper
+    {
+    enum { protector = false };
+    static T declval_noref();
+    };
+
+  static_assert(helper::protector, "declval_noref() must not be called");
+  return helper::declval_noref();
+  }
+
+void
+f (void)
+  {
+  constexpr int b2 = 0;
+  a = __builtin_choose_expr (b, c++, ++d);
+  a = __builtin_choose_expr (b2, --c, d--);
+  a = __builtin_choose_expr (s<b2>::value, c, d);
+  a = __builtin_choose_expr (__builtin_choose_expr(b, b2, 1),
+                             __builtin_choose_expr(b, c, d),
+                             __builtin_choose_expr(b, c, d));
+  }
+
+CHECK(__builtin_choose_expr(0, 1, 2.0), double, 2.0);
+CHECK(__builtin_choose_expr(1, 1, 2.0), int,    1  );
+CHECK(__builtin_choose_expr(0, cause error, 1), int, 1);
+CHECK(__builtin_choose_expr(1, 1, cause error), int, 1);
+
+constexpr auto c1 = __builtin_choose_expr(0, 1, 2.0);
+constexpr auto c2 = __builtin_choose_expr(1, 1, 2.0);
+constexpr auto c3 = __builtin_choose_expr(__builtin_choose_expr(0, 1, 0), 1, 2.0);
+constexpr auto c4 = __builtin_choose_expr(__builtin_choose_expr(1, 1, 0), 1, 2.0);
+static const auto c5 = [](){ return __builtin_choose_expr(0, 1, 2.0); }();
+static const auto c6 = [](){ return __builtin_choose_expr(1, 1, 2.0); }();
+
+CHECK(c1, const double, 2.0);
+CHECK(c2, const int,    1  );
+CHECK(c3, const double, 2.0);
+CHECK(c4, const int,    1  );
+CHECK_TYPE(c5, const double);
+CHECK_TYPE(c6, const int   );
+
+template <int i>
+constexpr int test1()
+ { return __builtin_choose_expr(i < 5, i * 2, i * 3); }
+
+CHECK(test1<1>(),  int, 2 );
+CHECK(test1<10>(), int, 30);
+
+struct test2
+  {
+  static constexpr int j = 0;
+  static constexpr int value = __builtin_choose_expr(j < 5, j + 2, j);
+
+  static constexpr auto fn1()
+            -> decltype(__builtin_choose_expr(j < 5, int(), double()))
+    { return __builtin_choose_expr(j < 5, 1, 2.0); }
+
+  static constexpr auto fn2()
+            -> decltype(__builtin_choose_expr(j > 5, int(), double()))
+    { return __builtin_choose_expr(j > 5, 1, 2.0); }
+  };
+
+CHECK(test2::value, const int, 2  );
+CHECK(test2::fn1(), int,       1  );
+CHECK(test2::fn2(), double,    2.0);
+
+template <int i>
+struct test3
+  {
+  static constexpr int j = i * 3;
+  static constexpr int value = __builtin_choose_expr(i < 5, i * 2, i * 3);
+
+  static constexpr auto fn()
+           -> decltype(__builtin_choose_expr(i, declval_noref<int>(), declval_noref<double>()))
+      { return __builtin_choose_expr(i, value, value); }
+
+  template <int j, int k>
+  struct child
+    {
+    /* nested template class, nested __builtin_choose_expr */
+    static constexpr int value = __builtin_choose_expr(
+                                      __builtin_choose_expr((i < 5), (j < 5), (k < 5)),
+                                      2 * __builtin_choose_expr((i < j), (j - i), (i - j)),
+                                      3 * __builtin_choose_expr((i < k), (k - i), (i - k)));
+    };
+  };
+
+CHECK(test3<1>::value,  const int, 2 );
+CHECK(test3<10>::value, const int, 30);
+CHECK(test3<1>::fn(),   int,       2 );
+CHECK(test3<10>::fn(),  int,       30);
+CHECK(test3<0>::fn(),   double,    0 );
+
+static_assert(test3<1>::child<3, 0>::value == 4, "oops");
+static_assert(test3<3>::child<1, 0>::value == 4, "oops");
+static_assert(test3<1>::child<5, 4>::value == 9, "oops");
+static_assert(test3<4>::child<5, 1>::value == 9, "oops");
+static_assert(test3<5>::child<4, 1>::value == 2, "oops");
+static_assert(test3<5>::child<6, 1>::value == 2, "oops");
+static_assert(test3<5>::child<0, 10>::value == 15, "oops");
+static_assert(test3<10>::child<0, 5>::value == 15, "oops");
+
+template <int i>
+__attribute__((gnu_inline)) /* necessary to avoid mangling */
+constexpr auto test4()
+           -> decltype(__builtin_choose_expr(i, 1, 2.0))
+ { return __builtin_choose_expr(i, 1, 2.0); }
+
+CHECK(test4<0>(), double, 2.0);
+CHECK(test4<1>(), int,    1  );
+
+struct T1;
+struct T2;
+
+template <typename T = decltype(__builtin_choose_expr(1, declval_noref<T1>(), declval_noref<T2>()))>
+struct X { typedef T type; };
+
+static_assert(is_same<typename X<>::type, T1>::value, "oops");
+
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr7.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr7.C
@@ -0,0 +1,87 @@ 
+/* Test for valid use of __builtin_choose_expr.  */
+/* { dg-do run } */
+/* { dg-options "-O1 -Wall -std=c++0x" } */
+
+extern "C" void abort ();
+extern "C" void exit (int);
+
+bool bad ()
+{
+  return false;
+}
+
+bool good ()
+{
+  return true;
+}
+
+template <typename T, T t>
+struct wrapper
+{
+  static constexpr T value = t;
+};
+
+struct T
+{
+  explicit constexpr T(int i) : i(i) { }
+  int i;
+  static bool t() { return true; }
+};
+
+struct S
+{
+  static bool s() { return true; }
+};
+
+#define TS(a,b) __builtin_choose_expr((a), b::t, b::s)
+
+#define TEST(c,v) __builtin_choose_expr((c), \
+          (wrapper<decltype(v),(v)>::value), \
+          [&](){ constexpr auto o = (v); return o; }())
+
+constexpr T t = T(TEST(1, 10));
+
+int main (void)
+{
+  T tt = TEST(0, t);
+
+  constexpr int one = 1;
+  float f;
+
+  __builtin_choose_expr (sizeof (int), f, bad ()) = 3.5F;
+
+  if (f != 3.5F)
+    abort ();
+
+  if (tt.i != 10)
+    abort ();
+
+  if (!__builtin_choose_expr (!one, bad, [=](){ return f == 3.5F; })())
+    abort ();
+
+  if (!__builtin_choose_expr (!one, bad, [&](){ return f == 3.5F; })())
+    abort ();
+
+  if (!__builtin_choose_expr (one, [](float g){ return g == 3.5F; }, bad)(f))
+    abort ();
+
+  if (!__builtin_choose_expr (one, good, bad)())
+    abort ();
+
+  if (!__builtin_choose_expr (!one, bad, good)())
+    abort ();
+
+  /* the following condition evaluates since the compiler can
+   * determine that `1' is always true and so `tt.i' need not
+   * be evaluated                                             */
+  if (!__builtin_choose_expr (1 ? 0 : tt.i, bad, good)())
+    abort ();
+
+  if (!TS(1, T)())
+    abort ();
+
+  if (!TS(0, S)())
+    abort ();
+
+  exit (0);
+}
--- gcc-4.7.0-r180072/gcc/testsuite/g++.dg/ext/builtin-choose-expr8.C
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr8.C
@@ -0,0 +1,107 @@ 
+/* Test for parsing of __builtin_choose_expr.  */
+/* { dg-do compile } */
+/* { dg-options "" } */
+
+#define JOIN(a,b) JOIN2(a,b)
+#define JOIN2(a,b) a ## b
+#define causes_error JOIN(causes_error_, __COUNTER__)
+
+#define STATIC_ASSERT(a) \
+  typedef char JOIN(assertion_failed_at_line_, __LINE__) \
+    [(a) ? 1 : -1]
+
+template <int i, int j>
+struct s {};
+
+struct A
+ {
+ static const int a = __builtin_choose_expr(0, 1, causes_error); /* { dg-error "causes_error" } */
+ static const int b = __builtin_choose_expr(1, 1, causes_error);
+ static const int c = __builtin_choose_expr(0, causes_error, 0);
+ static const int d = __builtin_choose_expr(1, causes_error, 0); /* { dg-error "causes_error" } */
+
+ static const int k = __builtin_choose_expr(b, 1, causes_error);
+ static const int l = __builtin_choose_expr(b, causes_error, 0); /* { dg-error "causes_error" } */
+ static const int o = __builtin_choose_expr(b, b, causes_error);
+ static const int p = __builtin_choose_expr(b, causes_error, b); /* { dg-error "causes_error" } */
+ };
+
+template <int I>
+struct B
+ {
+ static const int a = __builtin_choose_expr(0, 1, causes_error); /* { dg-error "causes_error" } */
+ static const int b = __builtin_choose_expr(1, 1, causes_error);
+ static const int c = __builtin_choose_expr(0, causes_error, 0);
+ static const int d = __builtin_choose_expr(1, causes_error, 0); /* { dg-error "causes_error" } */
+
+ static const int e = __builtin_choose_expr(I, 1, causes_error); /* { dg-error "causes_error" } */
+ static const int f = __builtin_choose_expr(I, causes_error, 0); /* { dg-error "causes_error" } */
+ static const int g = __builtin_choose_expr(I, I, causes_error); /* { dg-error "causes_error" } */
+ static const int h = __builtin_choose_expr(I, causes_error, I); /* { dg-error "causes_error" } */
+ static const int i = __builtin_choose_expr(I, b, causes_error); /* { dg-error "causes_error" } */
+ static const int j = __builtin_choose_expr(I, causes_error, b); /* { dg-error "causes_error" } */
+
+ static const int k = __builtin_choose_expr(b, 1, causes_error);
+ static const int l = __builtin_choose_expr(b, causes_error, 0); /* { dg-error "causes_error" } */
+ static const int m = __builtin_choose_expr(b, I, causes_error);
+ static const int n = __builtin_choose_expr(b, causes_error, I); /* { dg-error "causes_error" } */
+ static const int o = __builtin_choose_expr(b, b, causes_error);
+ static const int p = __builtin_choose_expr(b, causes_error, b); /* { dg-error "causes_error" } */
+
+ static const int q = I;
+ static const int r = __builtin_choose_expr(q, 1, causes_error); /* { dg-error "causes_error" } */
+ static const int s = __builtin_choose_expr(q, causes_error, 0); /* { dg-error "causes_error" } */
+ static const int t = __builtin_choose_expr(q, I, causes_error); /* { dg-error "causes_error" } */
+ static const int u = __builtin_choose_expr(q, causes_error, I); /* { dg-error "causes_error" } */
+ static const int v = __builtin_choose_expr(q, b, causes_error); /* { dg-error "causes_error" } */
+ static const int w = __builtin_choose_expr(q, causes_error, b); /* { dg-error "causes_error" } */
+};
+
+void f()
+ {
+ A aa;
+
+ B<0> ab;
+ B<1> ac;
+
+ STATIC_ASSERT(aa.o == 1);
+ STATIC_ASSERT(ab.m == 0);
+ STATIC_ASSERT(ac.m == 1);
+ STATIC_ASSERT(ab.o == 1);
+ STATIC_ASSERT(ac.o == 1);
+
+ (void)__builtin_choose_expr(0, 1, causes_error); /* { dg-error "causes_error" } */
+ (void)__builtin_choose_expr(1, 1, causes_error);
+ (void)__builtin_choose_expr(0, causes_error, 0);
+ (void)__builtin_choose_expr(1, causes_error, 0); /* { dg-error "causes_error" } */
+
+  /* Test for expressions containing comma in strings or character literals */
+ (void)__builtin_choose_expr(1, ',', 0);
+ (void)__builtin_choose_expr(1, ",", 0);
+ (void)__builtin_choose_expr(0, ',', 0);
+ (void)__builtin_choose_expr(0, ",", 0);
+ (void)__builtin_choose_expr(1, 0, ',');
+ (void)__builtin_choose_expr(1, 0, ",");
+ (void)__builtin_choose_expr(0, 0, ',');
+ (void)__builtin_choose_expr(0, 0, ",");
+
+  /* Test for expressions containing commas inside brackets */
+ (void)__builtin_choose_expr(0, [,], 1);
+ (void)__builtin_choose_expr(1, 1, [,]);
+ (void)__builtin_choose_expr(0, {,}, 1);
+ (void)__builtin_choose_expr(1, 1, {,});
+ (void)__builtin_choose_expr(0, (,), 1);
+ (void)__builtin_choose_expr(1, 1, (,));
+ (void)__builtin_choose_expr(1, s<0,1>(), 1);
+ (void)__builtin_choose_expr(0, 1, s<0,1>());
+ (void)__builtin_choose_expr(0, (s<0,1>()), 1);
+ (void)__builtin_choose_expr(1, (s<0,1>()), 1);
+ (void)__builtin_choose_expr(0, 1, (s<0,1>()));
+ (void)__builtin_choose_expr(1, 1, (s<0,1>()));
+
+ /* The following are undesired fails due to lexer not being able to
+    differentiate between s<0 as a comparison expression and s<0,1>()
+    as a template class contructor expression                         */
+ (void)__builtin_choose_expr(0, s<0,1>(), 1); /* { dg-error "expected" } */
+ (void)__builtin_choose_expr(1, 1, s<0,1>()); /* { dg-error "expected" } */
+ }