Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

repeating a VLA type results in a different and incompatible type each time #23148

Open Quuxplusone opened 9 years ago

Quuxplusone commented 9 years ago
Bugzilla Link PR23149
Status NEW
Importance P normal
Reported by Alexander (sasho648@gmail.com)
Reported on 2015-04-07 11:10:02 -0700
Last modified on 2020-11-15 18:32:58 -0800
Version trunk
Hardware PC Linux
CC CS15BTECH11029@iith.ac.in, jhart.public@gmail.com, josephcsible@gmail.com, llvm-bugs@lists.llvm.org, paul_robinson@playstation.sony.com, richard-llvm@metafoo.co.uk, rnk@google.com, tangyixuan@mail.dlut.edu.cn
Fixed by commit(s)
Attachments
Blocks
Blocked by
See also
The problem is that I can't assign VLA pointer or reference to a variable with
explicit type. Examples:

void func(std::size_t sz)
{
    int arr[sz];

    int (*pArr)[sz] = &arr;

    int (&refArr)[sz] = arr;
}

It fails to compile with those strange error message:

error: cannot initialize a variable of type 'int (*)[sz]' with an rvalue of
type 'int (*)[sz]'
    int (*pArr)[sz] = &arr;

error: non-const lvalue reference to type 'int [sz]' cannot bind to a value of
unrelated type 'int [sz]'
    int (&refArr)[sz] = arr;

On the other hand this works fine:

void func(std::size_t sz)
{
    int arr[sz];

    auto *pArr = &arr;

    auto &refArr = arr;
}

While reference to VLA arrays are not part of C99, pointers to them are well-
defined and should be supported if compatibility with this standard is
supported. However I hope reference will be kept too (as they are a cool add-
on).

Those constructs are well supported by GCC compiler too.

Life examples on online-compiler with Clang(3.7.0):

Non-working example:

http://melpon.org/wandbox/permlink/ZIUJrZqmvBmDzfkC

Working example, second one:

http://melpon.org/wandbox/permlink/gSXxvRXQRaCBzdwN
Quuxplusone commented 9 years ago
The same applies for function parameters:

void func(size_t sz, int (*arr)[sz])
{
    printf("%lu", sizeof(*arr) / sizeof(int));
}

int main()
{
    size_t sz = 3;

    int arr[sz];

    func(sz, &arr);
}

This code fails with:

note: candidate function not viable: no known conversion from 'int (*)[sz]' to
'int (*)[sz]' for 2nd argument
void func(size_t sz, int (*arr)[sz])

Life example:

http://melpon.org/wandbox/permlink/ygeV7ELkk49jRZFS
Quuxplusone commented 9 years ago
The same applies for function parameters:

void func(size_t sz, int (*arr)[sz])
{
    printf("%lu", sizeof(*arr) / sizeof(int));
}

int main()
{
    size_t sz = 3;

    int arr[sz];

    func(sz, &arr);
}

This code fails with:

note: candidate function not viable: no known conversion from 'int (*)[sz]' to
'int (*)[sz]' for 2nd argument
void func(size_t sz, int (*arr)[sz])

Life example:

http://melpon.org/wandbox/permlink/ygeV7ELkk49jRZFS
Quuxplusone commented 9 years ago

I think a better diagnostic here would be nice, but I don't think we'll go to great lengths to support this.

Quuxplusone commented 9 years ago

Any development on the issue?

Quuxplusone commented 4 years ago

_Bug 47234 has been marked as a duplicate of this bug._

Quuxplusone commented 4 years ago

_Bug 23976 has been marked as a duplicate of this bug._

Quuxplusone commented 4 years ago

_Bug 24259 has been marked as a duplicate of this bug._

Quuxplusone commented 4 years ago

_Bug 37828 has been marked as a duplicate of this bug._

Quuxplusone commented 4 years ago
Pulling in commentary from the various duplicate bugs:

Each time you write a VLA type, you get a different type.

1) The situation in C:

In C, assignment is permitted between pointers to compatible types, and
different VLA types with compatible element types are considered compatible
(although behavior is undefined if the bound evaluates to a different value).

In C, we reject this valid code under -pedantic-errors:

  void f(int *mem, int n, int m) {
    int (*array)[n][m] = *(int (*)[n][m])(mem);
  }

This is valid under C11 6.7.6.2/6. We should permit conversions that change
between different VLA bounds or between a VLA bound and a constant bound
(presumably in both C and C++).

(And while we're there, we should add a UBSan check that the conversion doesn't
*actually* change the bound.)

2) The situation in C++:

VLA support in C++ is an extension, so there is no "right" or "wrong" here. But
compatibility with other compilers and with reasonable code is nonetheless a
goal.

In C++, assignment is only permitted between convertible types; there is no
notion of type compatibility, and we do not permit implicit conversions between
arrays with different bounds. If we want to address this, that's the place to
do it -- we could permit conversion between pointers to VLA types and pointers
to arbitrary array types with the same element type, and rely on the user to
get the size right, like C does. But our current approach is simply to require
the user to use an explicit cast in all such situations where the type doesn't
exactly match.

3) Workaround / fix

Broadly, the way to avoid this issue is to only write the VLA type once. For
example, you can do this by using a typedef:

#include <stdlib.h>
void allocateArray(int height) {
    typedef float VLA[height];
    VLA *Ix;
    Ix = (VLA*) malloc(sizeof(VLA)); // OK, same VLA type both times
}

You can also sometimes do this by using an auto type:

    auto *Ix = (VLA*) malloc(sizeof(VLA)); // (C++)
    __auto_type *Ix = (VLA*) malloc(sizeof(VLA)); // (C)
Quuxplusone commented 3 years ago

Is there a workaround for this that works for parameters to functions? For example, how would you call a function declared as "void f(int rows, int cols, int arr[rows][cols])"?

Quuxplusone commented 3 years ago
(In reply to Joseph C. Sible from comment #10)
> Is there a workaround for this that works for parameters to functions? For
> example, how would you call a function declared as "void f(int rows, int
> cols, int arr[rows][cols])"?

You can use __typeof for that:

void f(int rows, int cols, int arr[rows][cols]) {
  __typeof(arr) *p = &arr;
  __typeof(arr[0]) *q = &arr[0];
}

(For 'p', decltype(arr) will work too.)
Quuxplusone commented 3 years ago
(In reply to Richard Smith from comment #11)
> (In reply to Joseph C. Sible from comment #10)
> > Is there a workaround for this that works for parameters to functions? For
> > example, how would you call a function declared as "void f(int rows, int
> > cols, int arr[rows][cols])"?
>
> You can use __typeof for that:
>
> void f(int rows, int cols, int arr[rows][cols]) {
>   __typeof(arr) *p = &arr;
>   __typeof(arr[0]) *q = &arr[0];
> }
>
> (For 'p', decltype(arr) will work too.)

That lets me declare more variables in the body of the function, but I still
don't have a way to actually call such a function.