Bug 71504 - [C++11] constexpr fails with multidimensional arrays
Summary: [C++11] constexpr fails with multidimensional arrays
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 7.0
: P3 normal
Target Milestone: 10.0
Assignee: Jakub Jelinek
URL:
Keywords: rejects-valid
: 89239 (view as bug list)
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2016-06-12 00:00 UTC by Martin Sebor
Modified: 2022-12-19 21:20 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work: 10.0
Known to fail: 4.9.3, 5.3.0, 6.1.0, 7.0
Last reconfirmed: 2016-07-10 00:00:00


Attachments
partial fix (1.95 KB, patch)
2019-09-27 21:47 UTC, Jason Merrill
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Martin Sebor 2016-06-12 00:00:37 UTC
All recent versions of G++ fail to compile the following well-formed program with the error below.  Clang compiles it fine, as does EDG eccp 4.11 (though eccp issues a bogus "subscript out of range" warning).

$ cat zzz.c && /home/msebor/build/gcc-trunk-svn/gcc/xgcc -B /home/msebor/build/gcc-trunk-svn/gcc -Wall -Wextra -Wpedantic -fpermissive -fdump-tree-gimple=/dev/stdout -xc++ zzz.c
typedef const char A4 [10];

constexpr A4 a [] = { "123", "123456", "123456789" };

constexpr int len (const char *s)
{
  return *s ? 1 + len (s + 1) : 0;
}

constexpr const char *s = a [0];
constexpr const char *t = (a + 2)[-2];

constexpr int n0 = len (s);
constexpr int n1 = len (t);

constexpr int n2 = len (a [0]);
constexpr int n3 = len ((a + 2)[-2]);

#define A(e) static_assert ((e), #e)

A (n0 == 3);
A (n0 == n1);
A (n0 == n2);
A (n0 == n3);

zzz.c:14:24:   in constexpr expansion of ‘len(((const char*)(& a)))’
zzz.c:14:26: error: accessing value of ‘a’ through a ‘const char’ glvalue in a constant expression
 constexpr int n1 = len (t);
                          ^
zzz.c:17:24:   in constexpr expansion of ‘len(((const char*)((const char (*)[10])(& a))))’
zzz.c:17:36: error: accessing value of ‘a’ through a ‘const char’ glvalue in a constant expression
 constexpr int n3 = len ((a + 2)[-2]);
                                    ^
Comment 1 Drea Pinski 2016-07-10 20:45:30 UTC
Confirmed.
Comment 2 Tom Westerhout 2017-07-14 17:50:29 UTC
Hi!


I'm working on a library that does a lot of work inside constexpr
functions. In simple cases like the example above it's relatively easy
to avoid this bug. In real life, however, it's almost impossible.

Although this bug is not considered important, it it important for
me. Currently, it is the only reason my library doesn't work with GCC.
I'd really appreciate it if this bug was assigned to someone. If there
are no volunteers, I'd be happy to help myself. Unfortunately, I'm not
familiar with GCC's code base at all. So some pointers to where I can
get started would be very helpful.


Tom
Comment 3 Martin Sebor 2017-09-18 17:08:35 UTC
(In reply to Tom Westerhout from comment #2)

Start by stepping through the GCC code that handles the test case and figuring out what triggers the error and what makes GCC accept the successful case.  Compile GCC for debugging (https://gcc.gnu.org/wiki/DebuggingGCC), load xgcc into GDB, set a breakpoint in function error(), and run it.  A relatively easy way to figure things out is to start two debugging sessions side by side, one with the successful test case and one with the failing one, step through them side by side, and make note of the differences.  To see the tree operand that's being processed call the GCC function debug_tree() on it.
Comment 4 Will Wray 2018-03-31 22:45:25 UTC
A motivating example for this error to be given higher priority.

An array_ref wrapper for multidimensional C-arrays T[M][N]...
providing constexpr access via an index operator[]
 -> returns array_ref<T> when T is an array type or
 -> returns T& otherwise for non-array T

https://wandbox.org/permlink/vcAokwqzk5tOF1ok


#include <type_traits>

template <typename T> struct array_ref;

template <typename T> using ref_t = std::conditional_t<
                                    std::is_array_v<T>, array_ref<T>, T&>;

template <typename T, unsigned N> struct array_ref<T[N]>
{
    T* a;  // T (&a)[N]; 
    using const_reference = const ref_t<T>;
    constexpr const_reference operator[](unsigned I) const { return {a[I]}; }
};

template <typename A> array_ref(A&) -> array_ref<A>;

constexpr int a2[2] = {1,2};
static_assert( array_ref{a2}[0] == 1 );

constexpr int a22[2][2] = {{1,2},{3,4}};
static_assert( array_ref{a22}[0][0] == 1 );

> error: non-constant condition for static assertion
 static_assert( array_ref{a22}[0][0] == 1 );
> error: accessing value of 'a22' through a 'const int' glvalue in a constant expression

Clang accepts. (MSVC untested.)

A workaround is to replace the pointer member with a reference member
(a reference member is less flexible and generally discouraged).
So GCC with this bug forces the less flexible implementation afaict -
I couldn't find a workaround that uses a pointer member.

Like std::array, array_ref is intended to provide array copy.
This is why >1D index operations return wrapped array_ref<T>
(the wandbox link includes some static_asserts to illustrate).

Constexpr is a requirement for my application;
a reasonable requirement for a wrapped C-array.

Other multi-dimensional array implementations must've hit this
Comment 5 Will Wray 2018-03-31 22:55:41 UTC
Oops -
I left the wandbox link on Clang...
Here's the GCC Head link:
https://wandbox.org/permlink/rfvn9VSwZgU6nerN
Comment 6 Will Wray 2018-04-01 17:23:00 UTC
A simpler example exhibiting this bogus error; an innocent, idiomatic,
pedantic, platonic pair of nested range-for loops iterating a 2D array:

g++ prog.cc -Wall -Wextra -std=c++14 -pedantic-errors

#include <type_traits>

template <typename A>
constexpr auto sum(A const& a)
{
    int tot = 0;
    for (auto& row : a)
        for (auto elem : row)
            tot += elem;
    return tot;
}

constexpr int const a22[2][2] = {{1,2},{3,4}};

static_assert( sum(a22) == 10, "badsum");

prog.cc:17:18:   in 'constexpr' expansion of 'sum<int [2][2]>(a22)'
prog.cc:17:24: error: accessing value of 'a22' through a 'const int' glvalue in a constant expression
Comment 7 Jason Merrill 2019-09-27 13:10:42 UTC
*** Bug 89239 has been marked as a duplicate of this bug. ***
Comment 8 Jason Merrill 2019-09-27 21:47:59 UTC
Created attachment 46968 [details]
partial fix

This fixes handling of references to the first object, but not later ones, so several of the testcases in this PR still fail.  Putting this aside for now.
Comment 9 Jakub Jelinek 2019-10-04 06:56:34 UTC
Author: jakub
Date: Fri Oct  4 06:56:02 2019
New Revision: 276563

URL: https://gcc.gnu.org/viewcvs?rev=276563&root=gcc&view=rev
Log:
	PR c++/71504
	* constexpr.c (cxx_fold_indirect_ref_1): New function.
	(cxx_fold_indirect_ref): Use it.

	* g++.dg/cpp0x/constexpr-array21.C: New test.
	* g++.dg/cpp1y/constexpr-array7.C: New test.
	* g++.dg/cpp1z/constexpr-array1.C: New test.

2019-10-04  Jason Merrill  <jason@redhat.com>

	PR c++/71504
	* g++.dg/cpp0x/constexpr-array20.C: New test.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C
    trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C
    trunk/gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C
    trunk/gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/constexpr.c
    trunk/gcc/testsuite/ChangeLog
Comment 10 Jason Merrill 2020-01-10 21:27:17 UTC
Fixed for GCC 10.
Comment 11 Jakub Jelinek 2020-05-07 11:56:20 UTC
GCC 10.1 has been released.
Comment 12 Richard Biener 2020-07-23 06:51:49 UTC
GCC 10.2 is released, adjusting target milestone.
Comment 13 Richard Biener 2021-04-08 12:02:15 UTC
GCC 10.3 is being released, retargeting bugs to GCC 10.4.
Comment 14 Jakub Jelinek 2022-06-28 10:32:22 UTC
GCC 10.4 is being released, retargeting bugs to GCC 10.5.
Comment 15 Drea Pinski 2022-12-19 21:20:20 UTC
.