Patchwork [C++] __builtin_choose_expr

login
register
mail settings
Submitter Andy Gibbs
Date Oct. 19, 2011, 11:35 a.m.
Message ID <BLU0-SMTP1438BADEAC0393EE4EB96B6E7EA0@phx.gbl>
Download mbox | patch
Permalink /patch/120596/
State New
Headers show

Comments

Andy Gibbs - Oct. 19, 2011, 11:35 a.m.
Andy Gibbs wrote on 18th October 2011 at 1:36 PM:
> Paolo Carlini wrote on 18th October 2011 at 12:00 PM:
>> 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.

Actually, I managed to get 3 common tests moved out of gcc.dg (pre-existing)
and g++.dg/ext (from my original patch) into c-c++-common.

> I will post a replacement patch later this afternoon.

And here it is...  Is it now ready to be committed to trunk?

Regards,
Andy
Andy Gibbs - Oct. 21, 2011, 9:57 a.m.
Hi,

Please can I "bump" this patch and ask for it to be approved and committed:
http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01711.html

The patch is to implement the C built-in function __builtin_choose_expr(...) 
in C++.

I'm afraid I am new to contributing to GCC, so I hope I am going about this 
the right way.  I also appreciate that this is a very busy mailing list, but 
would be very grateful if the patch could be committed so that it can make 
it into 4.7.0.

Many thanks

Andy
Richard Guenther - Oct. 21, 2011, 10:04 a.m.
On Fri, Oct 21, 2011 at 11:57 AM, Andy Gibbs <andyg1001@hotmail.co.uk> wrote:
> Hi,
>
> Please can I "bump" this patch and ask for it to be approved and committed:
> http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01711.html
>
> The patch is to implement the C built-in function __builtin_choose_expr(...)
> in C++.
>
> I'm afraid I am new to contributing to GCC, so I hope I am going about this
> the right way.  I also appreciate that this is a very busy mailing list, but
> would be very grateful if the patch could be committed so that it can make
> it into 4.7.0.

What's the motivation for this?  Why can't it be implemented using C++
features such as templates and specialization?

Richard.


> Many thanks
>
> Andy
>
>
>
>
Andy Gibbs - Oct. 21, 2011, 1:54 p.m.
On Friday, October 21, 2011 12:04 PM, Richard Guenther wrote:
> What's the motivation for this?  Why can't it be implemented using C++
> features such as templates and specialization?

Sorry for the delay in my reply.

The motivation, simply put, is to be able to create both templates and 
macros that choose between two or more courses of action at compile time and 
do not generate code for the unchosen action(s), nor (importantly) cause 
compiler errors if the unchosen action(s) should be ill-formed.  The end aim 
is in the generation of source code libraries for compile-time evaluation.

An example in the template domain would be, having template specialisation 
without actually needing to specialise the template.  Ordinarily, 
specialising a template requires a copy to be made and modified according to 
the specialisation.  If the "base" template is changed, the specialisation 
also needs to be so adapted.  Missing one specialisation in a long list 
leads to unpredictable problems (yes, there is bitter experience here!).  If 
the majority of code is identical then it makes sense to try to either 
factor out the identical code and use some meta-template If struct or other 
such magic to limit the scope of the template specialisation.  This can 
become brain-bending if not impossible if, for example, any of the relevant 
expressions cannot be parameters into a meta-template construct.  It can 
also make the code very difficult to follow and debug.  In these cases, 
__builtin_choose_expr can be beneficial to avoid the need to specialise, 
while keeping the clarity and breadth of the template's intended function.

When it comes to macros, the use of __builtin_choose_expr can be very useful 
where one of the expressions would cause a compiler error.  An example of 
such a macro is as follows:

#define CONST(v) \
   __builtin_choose_expr(util::can_use_const_wrapper<decltype(v)>::value, \
       (util::const_wrapper<decltype(v),(v)>::value) : \
       ([&](){ constexpr auto o = (v); return o; })())

namespace util
 {
 template <typename T, T t>
 struct const_wrapper
  { static constexpr T value = t; };

 template <typename T>
 struct can_use_const_wrapper; /* code removed to keep example short! */
 /* value = true for types that can be used in template value parameters */
 }

This is a part of a larger library of code for constexpr functions, a 
majority of which implement algorithms in such a way that the compiler can 
evaluate them but which if evaluated at run-time would have a huge 
performance hit (e.g. CRC calculation on a string).  In this library, a 
second macro FORCE_CONSTEXPR is used to ensure these functions cannot be 
called at run-time or even inlined, but only evaluated at compile-time.  The 
CONST macro is then used, among other things, to hint to the compiler that 
it needs to evaluate at compile-time.

If __builtin_choose_expr in the CONST macro was replaced with "? :" then the 
code using it would fail to compile in certain situations, notably because 
C++ only permits certain types as template value parameters.  However, it is 
not sufficient either to simply use the lambda expression alone since this 
fails with compiler errors in other situations.  This is, I think, one 
example where it is not possible to use templates to get round the 
problem?...

There is a further use of __builtin_choose_expr in this constexpr library, 
which is similar to its use in C, which is to enable a macro function to 
wrap and choose between a compile-time version of the algorithm and a 
run-time version of the algorithm, for example:

#define CrcString(str) (__builtin_choose_expr(__builtin_constant_p(str), \
                                       CONST(CrcString_compiler(str)),   \
                                       CrcString_runtime(str)))

auto string = CrcString("test");

This might of course be implemented using "? :", but only if 
CrcString_compiler(...) and CrcString_runtime(...) return compatible types, 
which need not be so with __builtin_choose_expr.

I hope this gives you some feeling for how __builtin_choose_expr may be 
used, or for that matter how I use it.  I expect that others will find much 
more impressive uses!

Thanks

Andy
Joseph S. Myers - Oct. 21, 2011, 2:42 p.m.
On Fri, 21 Oct 2011, Andy Gibbs wrote:

> Hi,
> 
> Please can I "bump" this patch and ask for it to be approved and committed:
> http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01711.html

Have you sent in your copyright assignment papers to the FSF?  The patch 
is large enough to need them.
Andy Gibbs - Oct. 21, 2011, 2:57 p.m.
On Friday, October 21, 2011 4:42 PM, Joseph S. Myers wrote:
> On Fri, 21 Oct 2011, Andy Gibbs wrote:
>
>> Hi,
>>
>> Please can I "bump" this patch and ask for it to be approved and 
>> committed:
>> http://gcc.gnu.org/ml/gcc-patches/2011-10/msg01711.html
>
> Have you sent in your copyright assignment papers to the FSF?  The patch
> is large enough to need them.
>

Yes, I have.

Regards,

Andy

Patch

=====

Changelog:

2011-10-19  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/gcc.dg/builtin-choose-expr.c: Moved...
        * testsuite/c-c++-common/builtin-choose-expr.c: ...to here
        * testsuite/gcc.dg/builtin-choose-expr-2.c: Moved...
        * testsuite/c-c++-common/builtin-choose-expr-2.c: ...to here
        * testsuite/gcc.dg/Wunused-var-2.c: Moved...
        * testsuite/c-c++-common/Wunused-var-15.c: ...to here
        * 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


--- 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,18 @@  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)
+{
+  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 +3303,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 +3636,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 +7243,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/gcc.dg/builtin-choose-expr.c
+++ /dev/null
@@ -1,90 +0,0 @@ 
-/* { 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 void abort ();
-extern void exit ();
-
-void bad ()
-{
-  abort ();
-}
-
-void good ()
-{
-  exit (0);
-}
-
-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 ();
-
-  __builtin_choose_expr (1, good, bad)();
-
-  exit (0);
-}
--- gcc-4.7.0-r180072/gcc/testsuite/gcc.dg/builtin-choose-expr-2.c
+++ /dev/null
@@ -1,12 +0,0 @@ 
-/* Test diagnostic for invalid use of __builtin_choose_expr.  */
-/* Origin: Joseph Myers <joseph@codesourcery.com> */
-/* { dg-do compile } */
-/* { dg-options "" } */
-
-int a, b, c, d;
-
-void
-f (void)
-{
-  a = __builtin_choose_expr (b, c, d); /* { dg-error "first argument to '__builtin_choose_expr' not a constant" } */
-}
--- gcc-4.7.0-r180072/gcc/testsuite/gcc.dg/Wunused-var-2.c
+++ /dev/null
@@ -1,19 +0,0 @@ 
-/* { dg-do compile } */
-/* { dg-options "-Wunused" } */
-
-int
-f1 (void)
-{
-  int a;
-  int b;
-  int c;
-  int d;
-  int e;
-  a = 1;
-  b = 2;
-  c = 3;
-  d = 4;
-  e = 5;
-  return sizeof (a) + ((__typeof (b)) 1) + __alignof__ (c)
-         + __builtin_choose_expr (1, d, e);
-}
--- /dev/null
+++ gcc-4.7.0-patched/gcc/testsuite/c-c++-common/builtin-choose-expr.c
@@ -0,0 +1,100 @@ 
+/* 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)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void abort ();
+void exit (int);
+#ifdef __cplusplus
+}
+#endif
+
+int bad ()
+{
+  return 0;
+}
+
+int good ()
+{
+  return 1;
+}
+
+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);
+}
--- /dev/null
+++ gcc-4.7.0-patched/gcc/testsuite/c-c++-common/builtin-choose-expr-2.c
@@ -0,0 +1,16 @@ 
+/* 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 "first argument to '__builtin_choose_expr' not a constant" } */
+  /* { dg-error "'b' cannot appear in a constant-expression" "" { target c++ } 10 } */
+  a = __builtin_choose_expr (1, c); /* { dg-error "expected ','" "" { target c++ } } */
+  /* { dg-error "wrong number of arguments" "" { target c } 12 } */
+  a = __builtin_choose_expr (1, b, c, d); /* { dg-error "expected '\\)'" "" { target c++ } } */
+  /* { dg-error "wrong number of arguments" "" { target c } 14 } */
+}
--- /dev/null
+++ gcc-4.7.0-patched/gcc/testsuite/c-c++-common/Wunused-var-15.c
@@ -0,0 +1,19 @@ 
+/* { 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" "" { target c++ } } */
+  a = 1;
+  b = 2;
+  c = 3;
+  d = 4;
+  e = 5;
+  return sizeof (a) + ((__typeof (b)) 1) + __alignof__ (c)
+         + __builtin_choose_expr (1, d, e);
+}
--- /dev/null
+++ 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 } */
+}
--- /dev/null
+++ 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);
+}
--- /dev/null
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr3.C
@@ -0,0 +1,140 @@ 
+/* 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");
--- /dev/null
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr4.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);
+}
--- /dev/null
+++ gcc-4.7.0-patched/gcc/testsuite/g++.dg/ext/builtin-choose-expr5.C
@@ -0,0 +1,113 @@ 
+/* 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 { static int x(); };
+
+int x() { return 0; }
+
+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;
+ int t = 0;
+
+ 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>::x()
+    as a template class static function expression                       */
+ (void)__builtin_choose_expr(0, s<0,1>::x(), 1); /* { dg-error "expected" } */
+ (void)__builtin_choose_expr(1, 1, s<0,1>::x()); /* { dg-error "expected" } */
+
+ /* Valid comparison expressions that look like template expressions */
+ (void)__builtin_choose_expr(0, t<0, 1>::x());
+ (void)__builtin_choose_expr(1, t<0, 1>::x());
+ }