This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: C++ PATCH for c++/69509 and c++/69516 (infinite recursion with invalid VLAs in constexpr functions)


On 01/28/2016 01:33 PM, Marek Polacek wrote:
This patch fixes some problems with VLAs and constexprs.  (The runtime aspect
of this matter is being tracked in PR69517.)  It does two things:

1) Changes build_vec_init slightly to prevent the compiler from getting into
    infinite recursion.  Currently, we emit the following sequence when we're
    initializing e.g. n = 1; int a[n] = { 1, 2 }; (cleanup_point junk omitted):

   int a[0:(sizetype) (SAVE_EXPR <(ssizetype) n + -1>)];
   (void) (int[0:(sizetype) (SAVE_EXPR <(ssizetype) n + -1>)] *)   int * D.2256;
   (void) (D.2256 = (int *) &a)
     int * D.2257;
   (void) (D.2257 = D.2256)
     long int D.2258;
   (void) (D.2258 = (long int) (SAVE_EXPR <(ssizetype) n + -1>)) // == 0
   (void) (*D.2257 = 1)
   (void)  ++D.2257
   (void)  --D.2258	// == -1
   (void) (*D.2257 = 2)
   (void)  ++D.2257
   (void)  --D.2258	// == -2

   while (1)
     {
       if (D.2258 == -1)	// never triggers
	goto <D.2261>;
       (void) (*D.2257 = 0)
       (void)  ++D.2257
       (void)  --D.2258;
     }
   <D.2261>:;
   ...

   So we first write the elements from the initializer and then we default-initialize
   any remaining elements.  But the iterator == -1 check is never true for invalid
   code, which means the compiler will get stuck in the while loop forever, leading
   to crash -- it tries to cxx_eval_* the body of the loop over and over again.

   Fixed by changing == -1 into <= -1; this should only ever happen for invalid code,
   but we don't want to ICE in any case.

   This also "fixes" the problem in PR69517 -- the program could get into infinite
   recursion as well, because it was evaluating the sequence above at runtime. But
   since it's invoking UB, it doesn't matter much.

2) Moves the check for "array subscript out of bound" a tad above, because for
    invalid VLAs we can't rely on the bool "found" -- e.g. for the example above
    it will find all indexes in the initializer, so "found" is true, which means
    we wouldn't get to the out-of-bounds check below.

I haven't had time to study the code but I did apply the patch
and play with it a bit.  It sure does fix the infinite loop/
recursion problem!

That said, while the test case below is rejected with the array
being of type int (as it should be), it's accepted as is, with
the struct.  I don't know enough to tell if it's because of
the change you mention or if it's a latent bug.

The test case is also accepted with an invalid argument to foo
(negative or excessive), both which should be rejected as well.

You're clearly good and efficient at fixing things.  I seem to
have a knack for breaking them.  Please let me know if I should
open a new bug for this.

  struct A {
    constexpr A (int = 0) { }
    constexpr operator int () const { return 0; }
  };

  constexpr int foo (int n) {
    A a [n] = { 1, 2, 3, 4, 5, 6 };
    return a [99];
  }

  struct B { unsigned b: foo (1) + 1; };

Martin


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]