https://gcc.gnu.org/g:78d19ea3fea308a08d2844de88d43154465daa78

commit r16-3466-g78d19ea3fea308a08d2844de88d43154465daa78
Author: Sirui Mu <msrlanc...@gmail.com>
Date:   Thu Aug 28 21:48:24 2025 +0800

    c++: array subscript with COND_EXPR as the array
    
    The following minimum reproducer would miscompile with vanilla gcc:
    
      extern int x[10], y[10];
      bool g();
      void f() { 0[g() ? x : y] = 1; }
    
    gcc would mistakenly treat the subexpression (g() ? x : y) as a prvalue and
    move that array to stack. The following assignment would then write to the
    stack instead of to the global arrays. When optimizations are enabled, this
    assignment is discarded by dse and gcc generates the following code for the
    f function:
    
      "_Z1fi":
            jmp     "_Z1gv"
    
    The miscompilation requires all the following conditions to be met:
    
      - The array subscription expression is written as idx[array], instead of
        the usual form array[idx];
      - The "array" part must be a ternary expression (COND_EXPR in gcc tree)
        and it must be an lvalue.
      - The code must be compiled with -fstrong-eval-order which is the default
        for -std=c++17 or later.
    
    The cause of the issue lies in cp_build_array_ref, where it mistakenly
    generates a COND_EXPR with ARRAY_TYPE to the IL when all the criteria above
    are met. This patch tries to resolve this issue. It moves the
    canonicalization step that transforms idx[array] to array[idx] early in
    cp_build_array_ref to ensure we handle these two forms of array subscription
    consistently.
    
    Tested on x86_64-linux.
    
    gcc/cp/ChangeLog:
    
            * typeck.cc (cp_build_array_ref): Handle 0[arr] earlier.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp1z/array-condition-expr.C: New test.
    
    Signed-off-by: Sirui Mu <msrlanc...@gmail.com>

Diff:
---
 gcc/cp/typeck.cc                                  | 31 +++++++++++++----------
 gcc/testsuite/g++.dg/cpp1z/array-condition-expr.C | 26 +++++++++++++++++++
 2 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 41a3f4cb7cbf..ccfe50d3c370 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -3973,7 +3973,6 @@ tree
 cp_build_array_ref (location_t loc, tree array, tree idx,
                    tsubst_flags_t complain)
 {
-  tree first = NULL_TREE;
   tree ret;
 
   if (idx == 0)
@@ -3987,6 +3986,21 @@ cp_build_array_ref (location_t loc, tree array, tree idx,
       || TREE_TYPE (idx) == error_mark_node)
     return error_mark_node;
 
+  /* 0[array] */
+  if (TREE_CODE (TREE_TYPE (idx)) == ARRAY_TYPE)
+    {
+      std::swap (array, idx);
+
+      tree first = NULL_TREE;
+      if (flag_strong_eval_order == 2 && TREE_SIDE_EFFECTS (array))
+       idx = first = save_expr (idx);
+      ret = cp_build_array_ref (loc, array, idx, complain);
+
+      if (first)
+       ret = build2 (COMPOUND_EXPR, TREE_TYPE (ret), first, ret);
+      return ret;
+    }
+
   /* If ARRAY is a COMPOUND_EXPR or COND_EXPR, move our reference
      inside it.  */
   switch (TREE_CODE (array))
@@ -4066,14 +4080,6 @@ cp_build_array_ref (location_t loc, tree array, tree idx,
 
   bool non_lvalue = convert_vector_to_array_for_subscript (loc, &array, idx);
 
-  /* 0[array] */
-  if (TREE_CODE (TREE_TYPE (idx)) == ARRAY_TYPE)
-    {
-      std::swap (array, idx);
-      if (flag_strong_eval_order == 2 && TREE_SIDE_EFFECTS (array))
-       idx = first = save_expr (idx);
-    }
-
   if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE)
     {
       tree rval, type;
@@ -4149,17 +4155,16 @@ cp_build_array_ref (location_t loc, tree array, tree 
idx,
       protected_set_expr_location (ret, loc);
       if (non_lvalue)
        ret = non_lvalue_loc (loc, ret);
-      if (first)
-       ret = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (ret), first, ret);
       return ret;
     }
 
   {
     tree ar = cp_default_conversion (array, complain);
     tree ind = cp_default_conversion (idx, complain);
+    tree first = NULL_TREE;
 
-    if (!processing_template_decl
-       && !first && flag_strong_eval_order == 2 && TREE_SIDE_EFFECTS (ind))
+    if (!processing_template_decl && flag_strong_eval_order == 2
+       && TREE_SIDE_EFFECTS (ind))
       ar = first = save_expr (ar);
 
     /* Put the integer in IND to simplify error checking.  */
diff --git a/gcc/testsuite/g++.dg/cpp1z/array-condition-expr.C 
b/gcc/testsuite/g++.dg/cpp1z/array-condition-expr.C
new file mode 100644
index 000000000000..6b59d7c9fe46
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/array-condition-expr.C
@@ -0,0 +1,26 @@
+// { dg-do run { target c++17 } }
+
+int x[10];
+int y[10];
+bool c() { return true; }
+
+void f(int i, int v)
+{
+  (c() ? x : y)[i] = v;
+}
+
+void g(int i, int v)
+{
+  i[c() ? x : y] = v;
+}
+
+int main()
+{
+  f(0, 1);
+  if (x[0] != 1)
+    __builtin_abort();
+
+  g(0, 2);
+  if (x[0] != 2)
+    __builtin_abort();
+}

Reply via email to