On 8/28/25 12:33 PM, Sirui Mu wrote:
Hello,

This is the 2nd version of the patch. Changes made since the initial version:
   - Implemented Jason's suggestions, move the declaration of first down.
   - Add signed-off-by to the commit.

Please preserve the rationale in later revisions of the patch; revision notes can go at the top, followed by a scissors line (-- 8< --), then the commit message.

Please wrap lines in the commit message at 76 columns so that they still fit in 80 columns with the 4 spaces of left padding from 'git log'. The subject line should be even shorter; I aim for 50 characters after "[PATCH] ", but this isn't a strict rule.

The commit message also needs ChangeLog entries; git gcc-commit-mklog automates most of that.

This time, I've gone ahead and made these adjustments, and pushed the following:

Thanks again.
From 78d19ea3fea308a08d2844de88d43154465daa78 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlanc...@gmail.com>
Date: Thu, 28 Aug 2025 21:48:24 +0800
Subject: [PATCH] c++: array subscript with COND_EXPR as the array
To: gcc-patches@gcc.gnu.org

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>
---
 gcc/cp/typeck.cc                              | 31 +++++++++++--------
 .../g++.dg/cpp1z/array-condition-expr.C       | 26 ++++++++++++++++
 2 files changed, 44 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/array-condition-expr.C

diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 41a3f4cb7cb..ccfe50d3c37 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 00000000000..6b59d7c9fe4
--- /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();
+}
-- 
2.50.1

Reply via email to